From bcd06a378467c62b5dc377b0fb24d46651f9e8d7 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Wed, 8 Apr 2026 18:29:27 -0700 Subject: [PATCH 01/14] update: add VBO NB --- .../workflows/valence_band_offset.ipynb | 769 ++++++++++++++++++ 1 file changed, 769 insertions(+) create mode 100644 other/materials_designer/workflows/valence_band_offset.ipynb diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb new file mode 100644 index 00000000..227ef64c --- /dev/null +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -0,0 +1,769 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Valence Band Offset (VBO)\n", + "\n", + "Calculate the valence band offset for a labeled interface using a DFT workflow on the Mat3ra platform. The notebook loads an interface from a folder, splits it into interface/left/right materials using the Mat3ra interface labels, strips labels required by Quantum ESPRESSO, saves all three materials into a materials set, and submits the VBO workflow.\n", + "\n", + "

Usage

\n", + "\n", + "1. Create and save an interface with labels (for example via `create_interface_with_min_strain_zsl.ipynb`).\n", + "1. Set the interface and calculation parameters in cells 1.2 and 1.3 below.\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 VBO result.\n", + "\n", + "## Summary\n", + "\n", + "This notebook submits the `Valence Band Offset (2D)` workflow with three materials in the required order: interface, left side, and right side.\n" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## 1. Set up the environment and parameters\n", + "### 1.1. Install packages (JupyterLite)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "\n", + "if sys.platform == \"emscripten\":\n", + " import micropip\n", + "\n", + " await micropip.install(\"mat3ra-api-examples\", deps=False)\n", + " await micropip.install(\"mat3ra-utils\")\n", + " from mat3ra.utils.jupyterlite.packages import install_packages\n", + "\n", + " await install_packages(\"api_examples\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "### 1.2. Set parameters\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "from mat3ra.ide.compute import QueueName\n", + "from mat3ra.made.tools.convert.interface_parts_enum import InterfacePartsEnum\n", + "\n", + "# 2. Auth and organization parameters\n", + "ORGANIZATION_NAME = None\n", + "\n", + "# 3. Material parameters\n", + "FOLDER = \"../uploads\"\n", + "INTERFACE_NAME = \"Interface\"\n", + "INTERFACE_SYSTEM_NAME = None # Defaults to the loaded interface name and is reused for the materials set\n", + "LEFT_SIDE_PART = InterfacePartsEnum.SUBSTRATE\n", + "RIGHT_SIDE_PART = InterfacePartsEnum.FILM\n", + "MATERIALS_SET_NAME = None # Defaults to INTERFACE_SYSTEM_NAME\n", + "\n", + "# 4. Workflow parameters\n", + "APPLICATION_NAME = \"espresso\"\n", + "WORKFLOW_NAME = \"Valence Band Offset (2D)\"\n", + "MY_WORKFLOW_NAME = \"Valence Band Offset (VBO)\"\n", + "\n", + "# 5. Compute parameters\n", + "CLUSTER_NAME = None\n", + "QUEUE_NAME = QueueName.D\n", + "PPN = 1\n", + "\n", + "# 6. Job parameters\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 specific VBO parameters\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "# Method parameters\n", + "PSEUDOPOTENTIAL_TYPE = \"us\" # \"us\" (ultrasoft), \"nc\" (norm-conserving), \"paw\"\n", + "FUNCTIONAL = \"pbe\" # for gga: \"pbe\", \"pbesol\"; for lda: \"pz\"\n", + "MODEL_SUBTYPE = \"gga\"\n", + "\n", + "# K-grid and k-path\n", + "SCF_KGRID = None # e.g. [8, 8, 1]\n", + "KPATH = None # e.g. [{\"point\": \"G\", \"steps\": 20}, {\"point\": \"M\", \"steps\": 20}]\n", + "\n", + "# Energy cutoffs\n", + "ECUTWFC = 40\n", + "ECUTRHO = 200\n" + ] + }, + { + "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`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "os.environ[\"API_PORT\"] = \"3000\"\n", + "os.environ[\"API_SECURE\"] = \"false\"\n", + "os.environ[\"API_HOST\"] = \"localhost\"\n", + "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"OZoKn4TRfEMQn2fn-04Cvd9y16voV0wNkzlua6wptIk\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "from utils.auth import authenticate\n", + "\n", + "await authenticate()\n" + ] + }, + { + "cell_type": "markdown", + "id": "10", + "metadata": {}, + "source": [ + "### 2.2. Initialize API client\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.api_client import APIClient\n", + "\n", + "client = APIClient.authenticate()\n", + "client\n" + ] + }, + { + "cell_type": "markdown", + "id": "12", + "metadata": {}, + "source": [ + "### 2.3. Select account\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "client.list_accounts()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "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}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "### 2.4. Select project\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "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})\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "## 3. Create materials\n", + "### 3.1. Load interface from local folder\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "from utils.jupyterlite import load_material_from_folder\n", + "from utils.visualize import visualize_materials as visualize\n", + "\n", + "interface = load_material_from_folder(FOLDER, INTERFACE_NAME)\n", + "if interface is None:\n", + " raise ValueError(f\"No interface containing '{INTERFACE_NAME}' was found in '{FOLDER}'.\")\n", + "\n", + "visualize(interface, repetitions=[1, 1, 1])\n", + "visualize(interface, repetitions=[1, 1, 1], rotation=\"-90x\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "### 3.2. Split interface into left and right materials using labels\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.made.tools.modify import interface_get_part\n", + "\n", + "left_part = LEFT_SIDE_PART\n", + "right_part = RIGHT_SIDE_PART\n", + "\n", + "system_name = INTERFACE_SYSTEM_NAME or interface.name\n", + "interface_material = interface.clone()\n", + "left_material = interface_get_part(interface, part=left_part)\n", + "right_material = interface_get_part(interface, part=right_part)\n", + "\n", + "materials_by_role = {\n", + " \"interface\": interface_material,\n", + " \"left\": left_material,\n", + " \"right\": right_material,\n", + "}\n", + "\n", + "print(f\"System name: {system_name}\")\n", + "print(f\"Left side part: {left_part.name}\")\n", + "print(f\"Right side part: {right_part.name}\")\n", + "visualize([materials_by_role[\"interface\"], materials_by_role[\"left\"], materials_by_role[\"right\"]], repetitions=[1, 1, 1])\n" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "### 3.3. Strip labels required by Quantum ESPRESSO\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "for material in materials_by_role.values():\n", + " material.basis.set_labels_from_list([])\n", + "\n", + "for role, material in materials_by_role.items():\n", + " print(f\"{role}: {material.name}, atoms={material.basis.number_of_atoms}, labels={len(material.basis.labels.values)}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "### 3.4. Save interface, left, and right materials to the platform and add them to a materials set\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.made.material import Material\n", + "from utils.api import get_or_create_material\n", + "\n", + "materials_set_name = MATERIALS_SET_NAME or system_name\n", + "existing_material_entries = client.materials.list({\"name\": materials_set_name, \"owner._id\": ACCOUNT_ID})\n", + "materials_set = next((item for item in existing_material_entries if item.get(\"isSet\")), None)\n", + "\n", + "if materials_set is None:\n", + " materials_set = client.materials.create_set({\"name\": materials_set_name, \"owner\": {\"_id\": ACCOUNT_ID}})\n", + " print(f\"✅ Created materials set: {materials_set['name']} ({materials_set['_id']})\")\n", + "else:\n", + " print(f\"♻️ Reusing materials set: {materials_set['name']} ({materials_set['_id']})\")\n", + "\n", + "saved_materials = {}\n", + "for role in [\"interface\", \"left\", \"right\"]:\n", + " material = materials_by_role[role]\n", + " saved_material_response = get_or_create_material(client, material, ACCOUNT_ID)\n", + " saved_material = Material.create(saved_material_response)\n", + " saved_materials[role] = saved_material\n", + " try:\n", + " client.materials.move_to_set(saved_material.id, \"\", materials_set[\"_id\"])\n", + " except Exception as exc:\n", + " print(f\"⚠️ Could not move {saved_material.name} to materials set: {exc}\")\n", + "\n", + "for role in [\"interface\", \"left\", \"right\"]:\n", + " saved_material = saved_materials[role]\n", + " print(f\"{role}: {saved_material.name} -> {saved_material.id} | formula={saved_material.formula}\")\n", + "\n", + "desired_material_names = {\n", + " \"interface\": system_name,\n", + " \"left\": f\"{saved_materials['left'].formula} Slab\",\n", + " \"right\": f\"{saved_materials['right'].formula} Slab\",\n", + "}\n", + "\n", + "for role, desired_name in desired_material_names.items():\n", + " saved_material = saved_materials[role]\n", + " if saved_material.name != desired_name:\n", + " updated_material_response = client.materials.update(saved_material.id, {\"name\": desired_name})\n", + " saved_materials[role] = Material.create(updated_material_response)\n", + "\n", + "ordered_material_pairs = sorted(\n", + " saved_materials.items(),\n", + " key=lambda item: ((item[1].formula or \"\"), item[1].name, item[1].id),\n", + ")\n", + "role_to_material_index = {role: index for index, (role, _) in enumerate(ordered_material_pairs)}\n", + "\n", + "print(\"Material order for VBO workflow:\")\n", + "for index, (role, material) in enumerate(ordered_material_pairs):\n", + " print(f\" {index}: {role} -> {material.name} ({material.formula})\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "25", + "metadata": {}, + "source": [ + "## 4. Configure workflow\n", + "### 4.1. Select application\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.ade.application import Application\n", + "from mat3ra.standata.applications import ApplicationStandata\n", + "\n", + "app_config = ApplicationStandata.get_by_name_first_match(APPLICATION_NAME)\n", + "app = Application(**app_config)\n", + "print(f\"Using application: {app.name}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "27", + "metadata": {}, + "source": [ + "### 4.2. Load workflow from Standata and preview it\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.standata.workflows import WorkflowStandata\n", + "from mat3ra.wode.workflows import Workflow\n", + "from utils.visualize import visualize_workflow\n", + "\n", + "def build_vbo_workflow(role_to_material_index, workflow_name):\n", + " workflow_config = WorkflowStandata.find_by_application_and_name(app.name, WORKFLOW_NAME)\n", + " workflow = Workflow.create(workflow_config)\n", + " workflow.name = workflow_name\n", + "\n", + " material_index_units = {\n", + " \"BS + Avg ESP (Interface)\": role_to_material_index[\"interface\"],\n", + " \"BS + Avg ESP (interface left)\": role_to_material_index[\"left\"],\n", + " \"BS + Avg ESP (interface right)\": role_to_material_index[\"right\"],\n", + " }\n", + "\n", + " for subworkflow in workflow.subworkflows:\n", + " target_index = material_index_units.get(subworkflow.name)\n", + " if target_index is None:\n", + " continue\n", + " for unit in subworkflow.units:\n", + " if getattr(unit, \"operand\", None) == \"MATERIAL_INDEX\":\n", + " unit.value = str(target_index)\n", + " subworkflow.set_unit(unit)\n", + " break\n", + "\n", + " return workflow, material_index_units\n", + "\n", + "workflow, material_index_units = build_vbo_workflow(role_to_material_index, MY_WORKFLOW_NAME)\n", + "\n", + "print(\"Workflow MATERIAL_INDEX mapping:\")\n", + "for name, index in material_index_units.items():\n", + " print(f\" {name}: {index}\")\n", + "\n", + "visualize_workflow(workflow)\n" + ] + }, + { + "cell_type": "markdown", + "id": "29", + "metadata": {}, + "source": [ + "### 4.3. Set model and its parameters (physics)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.mode.model import Model\n", + "from mat3ra.standata.model_tree import ModelTreeStandata\n", + "\n", + "model_config = ModelTreeStandata.get_model_by_parameters(\n", + " type=\"dft\", subtype=MODEL_SUBTYPE, functional=FUNCTIONAL\n", + ")\n", + "model_config[\"method\"] = {\"type\": \"pseudopotential\", \"subtype\": PSEUDOPOTENTIAL_TYPE}\n", + "model = Model.create(model_config)\n", + "\n", + "for subworkflow in workflow.subworkflows:\n", + " if subworkflow.application.name == APPLICATION_NAME:\n", + " subworkflow.model = model\n" + ] + }, + { + "cell_type": "markdown", + "id": "31", + "metadata": {}, + "source": [ + "### 4.4. Modify method (computational parameters): k-grid, k-path, and cutoffs\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.wode.context.providers import (\n", + " PlanewaveCutoffsContextProvider,\n", + " PointsGridDataProvider,\n", + " PointsPathDataProvider,\n", + ")\n", + "\n", + "for subworkflow in workflow.subworkflows:\n", + " if subworkflow.application.name != APPLICATION_NAME:\n", + " continue\n", + "\n", + " unit_names = [unit.name for unit in subworkflow.units]\n", + "\n", + " if SCF_KGRID is not None and \"pw_scf\" in unit_names:\n", + " unit = subworkflow.get_unit_by_name(name=\"pw_scf\")\n", + " unit.add_context(PointsGridDataProvider(dimensions=SCF_KGRID, isEdited=True).yield_data())\n", + " subworkflow.set_unit(unit)\n", + "\n", + " if KPATH is not None and \"pw_bands\" in unit_names:\n", + " unit = subworkflow.get_unit_by_name(name=\"pw_bands\")\n", + " unit.add_context(PointsPathDataProvider(path=KPATH, isEdited=True).yield_data())\n", + " subworkflow.set_unit(unit)\n", + "\n", + " cutoffs_context = PlanewaveCutoffsContextProvider(\n", + " wavefunction=ECUTWFC, density=ECUTRHO, isEdited=True\n", + " ).yield_data()\n", + " for unit_name in [\"pw_scf\", \"pw_bands\"]:\n", + " if unit_name not in unit_names:\n", + " continue\n", + " unit = subworkflow.get_unit_by_name(name=unit_name)\n", + " unit.add_context(cutoffs_context)\n", + " subworkflow.set_unit(unit)\n" + ] + }, + { + "cell_type": "markdown", + "id": "33", + "metadata": {}, + "source": [ + "### 4.5. Preview final workflow\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34", + "metadata": {}, + "outputs": [], + "source": [ + "visualize_workflow(workflow)\n", + "workflow.to_dict()\n" + ] + }, + { + "cell_type": "markdown", + "id": "35", + "metadata": {}, + "source": [ + "### 4.6. Workflow ready for job creation\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Workflow prepared for embedding in the job payload.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "37", + "metadata": {}, + "source": [ + "## 5. Create the compute configuration\n", + "### 5.1. Get list of clusters\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38", + "metadata": {}, + "outputs": [], + "source": [ + "clusters = client.clusters.list()\n", + "print(f\"Available clusters: {[c['hostname'] for c in clusters]}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "39", + "metadata": {}, + "source": [ + "### 5.2. Create compute configuration\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40", + "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", + "else:\n", + " cluster = clusters[0]\n", + "\n", + "compute = Compute(\n", + " cluster=cluster,\n", + " queue=QUEUE_NAME,\n", + " ppn=PPN\n", + ")\n", + "compute\n" + ] + }, + { + "cell_type": "markdown", + "id": "41", + "metadata": {}, + "source": [ + "## 6. Create the job\n", + "### 6.1. Create job\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42", + "metadata": {}, + "outputs": [], + "source": [ + "from utils.api import create_job\n", + "from utils.generic import dict_to_namespace\n", + "from utils.visualize import display_JSON\n", + "\n", + "ordered_saved_materials = [material for _, material in ordered_material_pairs]\n", + "\n", + "print(\"Material order for VBO workflow submission:\")\n", + "for index, (role, material) in enumerate(ordered_material_pairs):\n", + " print(f\" {index}: {role} -> {material.name} ({material.id})\")\n", + "print(f\"Project: {project_id}\")\n", + "\n", + "job_name = f\"{MY_WORKFLOW_NAME} {system_name} {timestamp}\"\n", + "job_workflow, job_material_index_units = build_vbo_workflow(role_to_material_index, job_name)\n", + "\n", + "job_response = create_job(\n", + " api_client=client,\n", + " materials=ordered_saved_materials,\n", + " workflow=job_workflow,\n", + " project_id=project_id,\n", + " owner_id=ACCOUNT_ID,\n", + " prefix=job_name,\n", + " compute=compute.to_dict(),\n", + ")\n", + "\n", + "job = dict_to_namespace(job_response)\n", + "job_id = job._id\n", + "\n", + "print(\"Workflow MATERIAL_INDEX mapping used for this job:\")\n", + "for name, index in job_material_index_units.items():\n", + " print(f\" {name}: {index}\")\n", + "print(\"✅ Job created successfully!\")\n", + "print(f\"Job ID: {job_id}\")\n", + "\n", + "display_JSON(job_response)\n" + ] + }, + { + "cell_type": "markdown", + "id": "43", + "metadata": {}, + "source": [ + "## 7. Submit the job and monitor the status\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44", + "metadata": {}, + "outputs": [], + "source": [ + "client.jobs.submit(job_id)\n", + "print(f\"✅ Job {job_id} submitted successfully!\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45", + "metadata": { + "jupyter": { + "is_executing": true + } + }, + "outputs": [], + "source": [ + "from utils.api import wait_for_jobs_to_finish_async\n", + "\n", + "await wait_for_jobs_to_finish_async(client.jobs, [job_id], poll_interval=POLL_INTERVAL)\n" + ] + }, + { + "cell_type": "markdown", + "id": "46", + "metadata": {}, + "source": [ + "## 8. Retrieve and visualize results\n", + "### 8.1. Valence Band Offset\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.prode import PropertyName\n", + "from utils.visualize import display_JSON\n", + "\n", + "vbo_data = client.properties.get_for_job(job_id, property_name=PropertyName.scalar.valence_band_offset.value)\n", + "display_JSON(vbo_data)\n", + "\n", + "if vbo_data:\n", + " value = vbo_data[0].get(\"value\")\n", + " units = vbo_data[0].get(\"units\", \"eV\")\n", + " print(f\"Valence band offset: {value} {units}\")\n", + "else:\n", + " print(\"No valence band offset property was found for this job yet.\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.11.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 9ae145c045fdf5e10146d6cc51cd46f6933382f8 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Wed, 8 Apr 2026 19:03:24 -0700 Subject: [PATCH 02/14] update: cleanups --- .../workflows/valence_band_offset.ipynb | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb index 227ef64c..a22b6bbd 100644 --- a/other/materials_designer/workflows/valence_band_offset.ipynb +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -146,7 +146,7 @@ "os.environ[\"API_PORT\"] = \"3000\"\n", "os.environ[\"API_SECURE\"] = \"false\"\n", "os.environ[\"API_HOST\"] = \"localhost\"\n", - "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"OZoKn4TRfEMQn2fn-04Cvd9y16voV0wNkzlua6wptIk\"" + "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"mQXVoKi91h3ZpcftAxjF4UmVfD12Dq1zuXjF_wfJuEX\"" ] }, { @@ -694,7 +694,7 @@ "metadata": {}, "outputs": [], "source": [ - "client.jobs.submit(job_id)\n", + "# client.jobs.submit(job_id)\n", "print(f\"✅ Job {job_id} submitted successfully!\")\n" ] }, @@ -702,11 +702,7 @@ "cell_type": "code", "execution_count": null, "id": "45", - "metadata": { - "jupyter": { - "is_executing": true - } - }, + "metadata": {}, "outputs": [], "source": [ "from utils.api import wait_for_jobs_to_finish_async\n", @@ -731,17 +727,13 @@ "outputs": [], "source": [ "from mat3ra.prode import PropertyName\n", - "from utils.visualize import display_JSON\n", "\n", - "vbo_data = client.properties.get_for_job(job_id, property_name=PropertyName.scalar.valence_band_offset.value)\n", - "display_JSON(vbo_data)\n", + "vbo_data = client.properties.get_for_job(job_id)\n", + "vbo_value = client.properties.get_for_job(job_id, property_name=PropertyName.scalar.valence_band_offset.value)\n", + "print(f\"Valence Band Offset (VBO) value: {vbo_value} eV\")\n", + "from utils.visualize import visualize_properties\n", "\n", - "if vbo_data:\n", - " value = vbo_data[0].get(\"value\")\n", - " units = vbo_data[0].get(\"units\", \"eV\")\n", - " print(f\"Valence band offset: {value} {units}\")\n", - "else:\n", - " print(\"No valence band offset property was found for this job yet.\")\n" + "visualize_properties(vbo_data, title=\"Band Gap\")" ] }, { From 405b4f4f869641e103a2d4eae38e047c11176e55 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Wed, 8 Apr 2026 19:16:56 -0700 Subject: [PATCH 03/14] update: adhere to existing structure (claude) --- .../workflows/valence_band_offset.ipynb | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb index a22b6bbd..9157c4cb 100644 --- a/other/materials_designer/workflows/valence_band_offset.ipynb +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -7,7 +7,7 @@ "source": [ "# Valence Band Offset (VBO)\n", "\n", - "Calculate the valence band offset for a labeled interface using a DFT workflow on the Mat3ra platform. The notebook loads an interface from a folder, splits it into interface/left/right materials using the Mat3ra interface labels, strips labels required by Quantum ESPRESSO, saves all three materials into a materials set, and submits the VBO workflow.\n", + "Calculate the valence band offset for a labeled interface using a DFT workflow on the Mat3ra platform.\n", "\n", "

Usage

\n", "\n", @@ -19,7 +19,14 @@ "\n", "## Summary\n", "\n", - "This notebook submits the `Valence Band Offset (2D)` workflow with three materials in the required order: interface, left side, and right side.\n" + "1. Set up the environment and parameters: install packages (JupyterLite only) and configure parameters for the interface, workflow, compute resources, and job.\n", + "1. Authenticate and initialize API client: authenticate via browser, initialize the client, then select account and project.\n", + "1. Create materials: load an interface from the `../uploads` folder, split it into interface/left/right parts using interface labels, strip labels required by Quantum ESPRESSO, and save all three materials to the platform in a materials set.\n", + "1. Configure workflow: select application, load the VBO workflow from Standata, assign materials to subworkflows by role, set model and computational parameters, and preview the workflow.\n", + "1. Configure compute: get list of clusters and create compute configuration with selected cluster, queue, and number of processors.\n", + "1. Create the job with materials and workflow configuration: assemble the job from materials, workflow, project, and compute configuration.\n", + "1. Submit the job and monitor the status: submit the job and wait for completion.\n", + "1. Retrieve results: get and display the valence band offset.\n" ] }, { @@ -131,7 +138,7 @@ "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`.\n" + "Authenticate in the browser and have credentials stored in environment variable \"OIDC_ACCESS_TOKEN\".\n" ] }, { @@ -140,14 +147,7 @@ "id": "8", "metadata": {}, "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"API_PORT\"] = \"3000\"\n", - "os.environ[\"API_SECURE\"] = \"false\"\n", - "os.environ[\"API_HOST\"] = \"localhost\"\n", - "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"mQXVoKi91h3ZpcftAxjF4UmVfD12Dq1zuXjF_wfJuEX\"" - ] + "source": [] }, { "cell_type": "code", @@ -563,9 +563,7 @@ "cell_type": "markdown", "id": "35", "metadata": {}, - "source": [ - "### 4.6. Workflow ready for job creation\n" - ] + "source": [] }, { "cell_type": "code", @@ -573,9 +571,7 @@ "id": "36", "metadata": {}, "outputs": [], - "source": [ - "print(\"Workflow prepared for embedding in the job payload.\")\n" - ] + "source": [] }, { "cell_type": "markdown", @@ -694,7 +690,7 @@ "metadata": {}, "outputs": [], "source": [ - "# client.jobs.submit(job_id)\n", + "client.jobs.submit(job_id)\n", "print(f\"✅ Job {job_id} submitted successfully!\")\n" ] }, @@ -733,7 +729,7 @@ "print(f\"Valence Band Offset (VBO) value: {vbo_value} eV\")\n", "from utils.visualize import visualize_properties\n", "\n", - "visualize_properties(vbo_data, title=\"Band Gap\")" + "visualize_properties(vbo_data, title=\"Valence Band Offset\")" ] }, { From b450356b6ec58614557488ab94b7fe76898016a3 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Wed, 8 Apr 2026 19:51:26 -0700 Subject: [PATCH 04/14] update: cleanups --- .../workflows/valence_band_offset.ipynb | 112 ++++++------------ 1 file changed, 38 insertions(+), 74 deletions(-) diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb index 9157c4cb..5f264288 100644 --- a/other/materials_designer/workflows/valence_band_offset.ipynb +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -82,14 +82,13 @@ "# 3. Material parameters\n", "FOLDER = \"../uploads\"\n", "INTERFACE_NAME = \"Interface\"\n", - "INTERFACE_SYSTEM_NAME = None # Defaults to the loaded interface name and is reused for the materials set\n", "LEFT_SIDE_PART = InterfacePartsEnum.SUBSTRATE\n", "RIGHT_SIDE_PART = InterfacePartsEnum.FILM\n", - "MATERIALS_SET_NAME = None # Defaults to INTERFACE_SYSTEM_NAME\n", + "MATERIALS_SET_NAME = None # Defaults to the loaded interface name\n", "\n", "# 4. Workflow parameters\n", "APPLICATION_NAME = \"espresso\"\n", - "WORKFLOW_NAME = \"Valence Band Offset (2D)\"\n", + "WORKFLOW_SEARCH_TERM = \"valence_band_offset\"\n", "MY_WORKFLOW_NAME = \"Valence Band Offset (VBO)\"\n", "\n", "# 5. Compute parameters\n", @@ -147,7 +146,14 @@ "id": "8", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "import os\n", + "\n", + "os.environ[\"API_PORT\"] = \"3000\"\n", + "os.environ[\"API_SECURE\"] = \"false\"\n", + "os.environ[\"API_HOST\"] = \"localhost\"\n", + "# os.environ[\"OIDC_ACCESS_TOKEN\"] = \"mQXVoKi91h3ZpcftAxjF4UmVfD12Dq1zuXjF_wfJuEX\"" + ] }, { "cell_type": "code", @@ -268,7 +274,7 @@ "id": "19", "metadata": {}, "source": [ - "### 3.2. Split interface into left and right materials using labels\n" + "### 3.2. Create materials from interface parts\n" ] }, { @@ -280,24 +286,21 @@ "source": [ "from mat3ra.made.tools.modify import interface_get_part\n", "\n", - "left_part = LEFT_SIDE_PART\n", - "right_part = RIGHT_SIDE_PART\n", - "\n", - "system_name = INTERFACE_SYSTEM_NAME or interface.name\n", + "system_name = MATERIALS_SET_NAME or interface.name\n", + "left_material = interface_get_part(interface, part=LEFT_SIDE_PART)\n", + "right_material = interface_get_part(interface, part=RIGHT_SIDE_PART)\n", "interface_material = interface.clone()\n", - "left_material = interface_get_part(interface, part=left_part)\n", - "right_material = interface_get_part(interface, part=right_part)\n", "\n", - "materials_by_role = {\n", - " \"interface\": interface_material,\n", - " \"left\": left_material,\n", - " \"right\": right_material,\n", - "}\n", + "for material in [left_material, right_material]:\n", + " material.basis.set_labels_from_list([])\n", + " material.name = f\"{material.formula} Slab\"\n", + "interface_material.basis.set_labels_from_list([])\n", + "interface_material.name = system_name\n", "\n", - "print(f\"System name: {system_name}\")\n", - "print(f\"Left side part: {left_part.name}\")\n", - "print(f\"Right side part: {right_part.name}\")\n", - "visualize([materials_by_role[\"interface\"], materials_by_role[\"left\"], materials_by_role[\"right\"]], repetitions=[1, 1, 1])\n" + "materials_by_role = {\"interface\": interface_material, \"left\": left_material, \"right\": right_material}\n", + "for role, material in materials_by_role.items():\n", + " print(f\" {role}: {material.name}\")\n", + "visualize(list(materials_by_role.values()), repetitions=[1, 1, 1])\n" ] }, { @@ -305,7 +308,7 @@ "id": "21", "metadata": {}, "source": [ - "### 3.3. Strip labels required by Quantum ESPRESSO\n" + "### 3.3. Save materials and add to a materials set\n" ] }, { @@ -314,21 +317,13 @@ "id": "22", "metadata": {}, "outputs": [], - "source": [ - "for material in materials_by_role.values():\n", - " material.basis.set_labels_from_list([])\n", - "\n", - "for role, material in materials_by_role.items():\n", - " print(f\"{role}: {material.name}, atoms={material.basis.number_of_atoms}, labels={len(material.basis.labels.values)}\")\n" - ] + "source": [] }, { "cell_type": "markdown", "id": "23", "metadata": {}, - "source": [ - "### 3.4. Save interface, left, and right materials to the platform and add them to a materials set\n" - ] + "source": [] }, { "cell_type": "code", @@ -340,52 +335,21 @@ "from mat3ra.made.material import Material\n", "from utils.api import get_or_create_material\n", "\n", - "materials_set_name = MATERIALS_SET_NAME or system_name\n", - "existing_material_entries = client.materials.list({\"name\": materials_set_name, \"owner._id\": ACCOUNT_ID})\n", - "materials_set = next((item for item in existing_material_entries if item.get(\"isSet\")), None)\n", - "\n", + "existing = client.materials.list({\"name\": system_name, \"owner._id\": ACCOUNT_ID})\n", + "materials_set = next((m for m in existing if m.get(\"isSet\")), None)\n", "if materials_set is None:\n", - " materials_set = client.materials.create_set({\"name\": materials_set_name, \"owner\": {\"_id\": ACCOUNT_ID}})\n", - " print(f\"✅ Created materials set: {materials_set['name']} ({materials_set['_id']})\")\n", - "else:\n", - " print(f\"♻️ Reusing materials set: {materials_set['name']} ({materials_set['_id']})\")\n", + " materials_set = client.materials.create_set({\"name\": system_name, \"owner\": {\"_id\": ACCOUNT_ID}})\n", + "print(f\"✅ Materials set: {materials_set['name']} ({materials_set['_id']})\")\n", "\n", "saved_materials = {}\n", - "for role in [\"interface\", \"left\", \"right\"]:\n", - " material = materials_by_role[role]\n", - " saved_material_response = get_or_create_material(client, material, ACCOUNT_ID)\n", - " saved_material = Material.create(saved_material_response)\n", - " saved_materials[role] = saved_material\n", - " try:\n", - " client.materials.move_to_set(saved_material.id, \"\", materials_set[\"_id\"])\n", - " except Exception as exc:\n", - " print(f\"⚠️ Could not move {saved_material.name} to materials set: {exc}\")\n", - "\n", - "for role in [\"interface\", \"left\", \"right\"]:\n", - " saved_material = saved_materials[role]\n", - " print(f\"{role}: {saved_material.name} -> {saved_material.id} | formula={saved_material.formula}\")\n", - "\n", - "desired_material_names = {\n", - " \"interface\": system_name,\n", - " \"left\": f\"{saved_materials['left'].formula} Slab\",\n", - " \"right\": f\"{saved_materials['right'].formula} Slab\",\n", - "}\n", - "\n", - "for role, desired_name in desired_material_names.items():\n", - " saved_material = saved_materials[role]\n", - " if saved_material.name != desired_name:\n", - " updated_material_response = client.materials.update(saved_material.id, {\"name\": desired_name})\n", - " saved_materials[role] = Material.create(updated_material_response)\n", - "\n", - "ordered_material_pairs = sorted(\n", - " saved_materials.items(),\n", - " key=lambda item: ((item[1].formula or \"\"), item[1].name, item[1].id),\n", - ")\n", - "role_to_material_index = {role: index for index, (role, _) in enumerate(ordered_material_pairs)}\n", + "for role, material in materials_by_role.items():\n", + " saved = Material.create(get_or_create_material(client, material, ACCOUNT_ID))\n", + " client.materials.move_to_set(saved.id, \"\", materials_set[\"_id\"])\n", + " saved_materials[role] = saved\n", + " print(f\" {role}: {saved.name} ({saved.id})\")\n", "\n", - "print(\"Material order for VBO workflow:\")\n", - "for index, (role, material) in enumerate(ordered_material_pairs):\n", - " print(f\" {index}: {role} -> {material.name} ({material.formula})\")\n" + "ordered_material_pairs = sorted(saved_materials.items(), key=lambda item: ((item[1].formula or \"\"), item[1].name, item[1].id))\n", + "role_to_material_index = {role: idx for idx, (role, _) in enumerate(ordered_material_pairs)}\n" ] }, { @@ -432,7 +396,7 @@ "from utils.visualize import visualize_workflow\n", "\n", "def build_vbo_workflow(role_to_material_index, workflow_name):\n", - " workflow_config = WorkflowStandata.find_by_application_and_name(app.name, WORKFLOW_NAME)\n", + " workflow_config = WorkflowStandata.filter_by_application(app.name).get_by_name_first_match(WORKFLOW_SEARCH_TERM)\n", " workflow = Workflow.create(workflow_config)\n", " workflow.name = workflow_name\n", "\n", From 779bfb075a9c0ed43ae1d7b617e2de849e873997 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Wed, 8 Apr 2026 20:15:33 -0700 Subject: [PATCH 05/14] update: cleanup (human) --- .../workflows/valence_band_offset.ipynb | 155 ++++++------------ 1 file changed, 47 insertions(+), 108 deletions(-) diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb index 5f264288..c62d3377 100644 --- a/other/materials_designer/workflows/valence_band_offset.ipynb +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -89,7 +89,7 @@ "# 4. Workflow parameters\n", "APPLICATION_NAME = \"espresso\"\n", "WORKFLOW_SEARCH_TERM = \"valence_band_offset\"\n", - "MY_WORKFLOW_NAME = \"Valence Band Offset (VBO)\"\n", + "MY_WORKFLOW_NAME = \"VBO\"\n", "\n", "# 5. Compute parameters\n", "CLUSTER_NAME = None\n", @@ -118,12 +118,12 @@ "source": [ "# Method parameters\n", "PSEUDOPOTENTIAL_TYPE = \"us\" # \"us\" (ultrasoft), \"nc\" (norm-conserving), \"paw\"\n", - "FUNCTIONAL = \"pbe\" # for gga: \"pbe\", \"pbesol\"; for lda: \"pz\"\n", + "FUNCTIONAL = \"pbe\" # for gga: \"pbe\", \"pbesol\"; for lda: \"pz\"\n", "MODEL_SUBTYPE = \"gga\"\n", "\n", "# K-grid and k-path\n", "SCF_KGRID = None # e.g. [8, 8, 1]\n", - "KPATH = None # e.g. [{\"point\": \"G\", \"steps\": 20}, {\"point\": \"M\", \"steps\": 20}]\n", + "KPATH = None # e.g. [{\"point\": \"G\", \"steps\": 20}, {\"point\": \"M\", \"steps\": 20}]\n", "\n", "# Energy cutoffs\n", "ECUTWFC = 40\n", @@ -262,11 +262,9 @@ "from utils.visualize import visualize_materials as visualize\n", "\n", "interface = load_material_from_folder(FOLDER, INTERFACE_NAME)\n", - "if interface is None:\n", - " raise ValueError(f\"No interface containing '{INTERFACE_NAME}' was found in '{FOLDER}'.\")\n", "\n", "visualize(interface, repetitions=[1, 1, 1])\n", - "visualize(interface, repetitions=[1, 1, 1], rotation=\"-90x\")\n" + "visualize(interface, repetitions=[1, 1, 1], rotation=\"-90x\")" ] }, { @@ -274,7 +272,8 @@ "id": "19", "metadata": {}, "source": [ - "### 3.2. Create materials from interface parts\n" + "### 3.2. Create materials from interface parts\n", + "Slabs are isolated based on labels, then labels removed as they are not compatible with Quantum ESPRESSO. The three materials (interface, left slab, right slab) are named and visualized." ] }, { @@ -286,7 +285,7 @@ "source": [ "from mat3ra.made.tools.modify import interface_get_part\n", "\n", - "system_name = MATERIALS_SET_NAME or interface.name\n", + "interface_system_name = MATERIALS_SET_NAME or interface.name\n", "left_material = interface_get_part(interface, part=LEFT_SIDE_PART)\n", "right_material = interface_get_part(interface, part=RIGHT_SIDE_PART)\n", "interface_material = interface.clone()\n", @@ -295,7 +294,7 @@ " material.basis.set_labels_from_list([])\n", " material.name = f\"{material.formula} Slab\"\n", "interface_material.basis.set_labels_from_list([])\n", - "interface_material.name = system_name\n", + "interface_material.name = interface_system_name\n", "\n", "materials_by_role = {\"interface\": interface_material, \"left\": left_material, \"right\": right_material}\n", "for role, material in materials_by_role.items():\n", @@ -317,28 +316,14 @@ "id": "22", "metadata": {}, "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "23", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "24", - "metadata": {}, - "outputs": [], "source": [ "from mat3ra.made.material import Material\n", "from utils.api import get_or_create_material\n", "\n", - "existing = client.materials.list({\"name\": system_name, \"owner._id\": ACCOUNT_ID})\n", + "existing = client.materials.list({\"name\": interface_system_name, \"owner._id\": ACCOUNT_ID})\n", "materials_set = next((m for m in existing if m.get(\"isSet\")), None)\n", "if materials_set is None:\n", - " materials_set = client.materials.create_set({\"name\": system_name, \"owner\": {\"_id\": ACCOUNT_ID}})\n", + " materials_set = client.materials.create_set({\"name\": interface_system_name, \"owner\": {\"_id\": ACCOUNT_ID}})\n", "print(f\"✅ Materials set: {materials_set['name']} ({materials_set['_id']})\")\n", "\n", "saved_materials = {}\n", @@ -348,13 +333,14 @@ " saved_materials[role] = saved\n", " print(f\" {role}: {saved.name} ({saved.id})\")\n", "\n", - "ordered_material_pairs = sorted(saved_materials.items(), key=lambda item: ((item[1].formula or \"\"), item[1].name, item[1].id))\n", + "ordered_material_pairs = sorted(saved_materials.items(),\n", + " key=lambda item: ((item[1].formula or \"\"), item[1].name, item[1].id))\n", "role_to_material_index = {role: idx for idx, (role, _) in enumerate(ordered_material_pairs)}\n" ] }, { "cell_type": "markdown", - "id": "25", + "id": "23", "metadata": {}, "source": [ "## 4. Configure workflow\n", @@ -364,7 +350,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -378,7 +364,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "25", "metadata": {}, "source": [ "### 4.2. Load workflow from Standata and preview it\n" @@ -387,7 +373,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -395,41 +381,16 @@ "from mat3ra.wode.workflows import Workflow\n", "from utils.visualize import visualize_workflow\n", "\n", - "def build_vbo_workflow(role_to_material_index, workflow_name):\n", - " workflow_config = WorkflowStandata.filter_by_application(app.name).get_by_name_first_match(WORKFLOW_SEARCH_TERM)\n", - " workflow = Workflow.create(workflow_config)\n", - " workflow.name = workflow_name\n", - "\n", - " material_index_units = {\n", - " \"BS + Avg ESP (Interface)\": role_to_material_index[\"interface\"],\n", - " \"BS + Avg ESP (interface left)\": role_to_material_index[\"left\"],\n", - " \"BS + Avg ESP (interface right)\": role_to_material_index[\"right\"],\n", - " }\n", - "\n", - " for subworkflow in workflow.subworkflows:\n", - " target_index = material_index_units.get(subworkflow.name)\n", - " if target_index is None:\n", - " continue\n", - " for unit in subworkflow.units:\n", - " if getattr(unit, \"operand\", None) == \"MATERIAL_INDEX\":\n", - " unit.value = str(target_index)\n", - " subworkflow.set_unit(unit)\n", - " break\n", - "\n", - " return workflow, material_index_units\n", - "\n", - "workflow, material_index_units = build_vbo_workflow(role_to_material_index, MY_WORKFLOW_NAME)\n", - "\n", - "print(\"Workflow MATERIAL_INDEX mapping:\")\n", - "for name, index in material_index_units.items():\n", - " print(f\" {name}: {index}\")\n", + "workflow_config = WorkflowStandata.filter_by_application(app.name).get_by_name_first_match(WORKFLOW_SEARCH_TERM)\n", + "workflow = Workflow.create(workflow_config)\n", + "workflow.name = MY_WORKFLOW_NAME\n", "\n", "visualize_workflow(workflow)\n" ] }, { "cell_type": "markdown", - "id": "29", + "id": "27", "metadata": {}, "source": [ "### 4.3. Set model and its parameters (physics)\n" @@ -438,7 +399,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -458,7 +419,7 @@ }, { "cell_type": "markdown", - "id": "31", + "id": "29", "metadata": {}, "source": [ "### 4.4. Modify method (computational parameters): k-grid, k-path, and cutoffs\n" @@ -467,7 +428,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -506,7 +467,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "31", "metadata": {}, "source": [ "### 4.5. Preview final workflow\n" @@ -515,31 +476,16 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "32", "metadata": {}, "outputs": [], "source": [ - "visualize_workflow(workflow)\n", - "workflow.to_dict()\n" + "visualize_workflow(workflow)\n" ] }, { "cell_type": "markdown", - "id": "35", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "36", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "37", + "id": "33", "metadata": {}, "source": [ "## 5. Create the compute configuration\n", @@ -549,7 +495,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "34", "metadata": {}, "outputs": [], "source": [ @@ -559,7 +505,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "35", "metadata": {}, "source": [ "### 5.2. Create compute configuration\n" @@ -568,7 +514,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -584,12 +530,12 @@ " queue=QUEUE_NAME,\n", " ppn=PPN\n", ")\n", - "compute\n" + "print(f\"Using cluster: {compute.cluster.hostname}, queue: {QUEUE_NAME}, ppn: {PPN}\")\n" ] }, { "cell_type": "markdown", - "id": "41", + "id": "37", "metadata": {}, "source": [ "## 6. Create the job\n", @@ -599,7 +545,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -608,19 +554,16 @@ "from utils.visualize import display_JSON\n", "\n", "ordered_saved_materials = [material for _, material in ordered_material_pairs]\n", + "job_name = f\"{MY_WORKFLOW_NAME} {interface_system_name} {timestamp}\"\n", + "workflow.name = job_name\n", "\n", - "print(\"Material order for VBO workflow submission:\")\n", - "for index, (role, material) in enumerate(ordered_material_pairs):\n", - " print(f\" {index}: {role} -> {material.name} ({material.id})\")\n", + "print(f\"Materials: {[m.id for m in ordered_saved_materials]}\")\n", "print(f\"Project: {project_id}\")\n", "\n", - "job_name = f\"{MY_WORKFLOW_NAME} {system_name} {timestamp}\"\n", - "job_workflow, job_material_index_units = build_vbo_workflow(role_to_material_index, job_name)\n", - "\n", "job_response = create_job(\n", " api_client=client,\n", " materials=ordered_saved_materials,\n", - " workflow=job_workflow,\n", + " workflow=workflow,\n", " project_id=project_id,\n", " owner_id=ACCOUNT_ID,\n", " prefix=job_name,\n", @@ -629,10 +572,6 @@ "\n", "job = dict_to_namespace(job_response)\n", "job_id = job._id\n", - "\n", - "print(\"Workflow MATERIAL_INDEX mapping used for this job:\")\n", - "for name, index in job_material_index_units.items():\n", - " print(f\" {name}: {index}\")\n", "print(\"✅ Job created successfully!\")\n", "print(f\"Job ID: {job_id}\")\n", "\n", @@ -641,7 +580,7 @@ }, { "cell_type": "markdown", - "id": "43", + "id": "39", "metadata": {}, "source": [ "## 7. Submit the job and monitor the status\n" @@ -650,7 +589,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -661,7 +600,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -672,7 +611,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "42", "metadata": {}, "source": [ "## 8. Retrieve and visualize results\n", @@ -682,24 +621,24 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "43", "metadata": {}, "outputs": [], "source": [ "from mat3ra.prode import PropertyName\n", - "\n", - "vbo_data = client.properties.get_for_job(job_id)\n", - "vbo_value = client.properties.get_for_job(job_id, property_name=PropertyName.scalar.valence_band_offset.value)\n", - "print(f\"Valence Band Offset (VBO) value: {vbo_value} eV\")\n", "from utils.visualize import visualize_properties\n", "\n", + "vbo_value = client.properties.get_for_job(job_id, property_name=PropertyName.scalar.valence_band_offset.value)[0]\n", + "print(f\"Valence Band Offset (VBO) value: {vbo_value['value']:.3f} eV\")\n", + "\n", + "vbo_data = client.properties.get_for_job(job_id)\n", "visualize_properties(vbo_data, title=\"Valence Band Offset\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "44", "metadata": {}, "outputs": [], "source": [] From eabcadf63593faab56740d3dd05c691d8eed9aa1 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Wed, 8 Apr 2026 20:33:08 -0700 Subject: [PATCH 06/14] update: cleanup names --- .../workflows/valence_band_offset.ipynb | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb index c62d3377..bc48e62f 100644 --- a/other/materials_designer/workflows/valence_band_offset.ipynb +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -152,7 +152,7 @@ "os.environ[\"API_PORT\"] = \"3000\"\n", "os.environ[\"API_SECURE\"] = \"false\"\n", "os.environ[\"API_HOST\"] = \"localhost\"\n", - "# os.environ[\"OIDC_ACCESS_TOKEN\"] = \"mQXVoKi91h3ZpcftAxjF4UmVfD12Dq1zuXjF_wfJuEX\"" + "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"Q-9wDdNt-QJwA5Q6XAZTU1wNdFfoEambJEw1kBa7K9z\"" ] }, { @@ -331,11 +331,7 @@ " saved = Material.create(get_or_create_material(client, material, ACCOUNT_ID))\n", " client.materials.move_to_set(saved.id, \"\", materials_set[\"_id\"])\n", " saved_materials[role] = saved\n", - " print(f\" {role}: {saved.name} ({saved.id})\")\n", - "\n", - "ordered_material_pairs = sorted(saved_materials.items(),\n", - " key=lambda item: ((item[1].formula or \"\"), item[1].name, item[1].id))\n", - "role_to_material_index = {role: idx for idx, (role, _) in enumerate(ordered_material_pairs)}\n" + " print(f\" {role}: {saved.name} ({saved.id})\")\n" ] }, { @@ -553,16 +549,16 @@ "from utils.generic import dict_to_namespace\n", "from utils.visualize import display_JSON\n", "\n", - "ordered_saved_materials = [material for _, material in ordered_material_pairs]\n", + "materials = list(saved_materials.values())\n", "job_name = f\"{MY_WORKFLOW_NAME} {interface_system_name} {timestamp}\"\n", "workflow.name = job_name\n", "\n", - "print(f\"Materials: {[m.id for m in ordered_saved_materials]}\")\n", + "print(f\"Materials: {[m.id for m in materials]}\")\n", "print(f\"Project: {project_id}\")\n", "\n", "job_response = create_job(\n", " api_client=client,\n", - " materials=ordered_saved_materials,\n", + " materials=materials,\n", " workflow=workflow,\n", " project_id=project_id,\n", " owner_id=ACCOUNT_ID,\n", From 6d7e2373649a12bb6e16cb7a6828172053ba3252 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Wed, 8 Apr 2026 20:33:22 -0700 Subject: [PATCH 07/14] update: multimaterial --- utils/api.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/utils/api.py b/utils/api.py index 3fcfffbd..b2959beb 100644 --- a/utils/api.py +++ b/utils/api.py @@ -238,7 +238,7 @@ def create_job( # Strip _id so the server uses the embedded workflow as-is instead of fetching from DB, # which would discard any unit-level context (kpath, kgrid, cutoffs, etc.). job_workflow_dict.pop("_id", None) - is_multimaterial = job_workflow_dict.get("isMultimaterial", False) + is_multimaterial = job_workflow_dict.get("isMultiMaterial", False) config = { "_project": {"_id": project_id}, @@ -248,7 +248,11 @@ def create_job( } if is_multimaterial: - config["_materials"] = [{"_id": mid} for mid in {md["_id"] for md in material_dicts}] + # Some API environments still validate `_material._id` even for + # multi-material workflows, so provide the first material as a + # compatibility fallback while preserving the full ordered list. + config["_material"] = {"_id": material_dicts[0]["_id"]} + config["_materials"] = [{"_id": m["_id"]} for m in material_dicts] else: config["_material"] = {"_id": material_dicts[0]["_id"]} From 71859227477150e1021ec97a30e466d0aae74bd4 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Thu, 9 Apr 2026 11:42:56 -0700 Subject: [PATCH 08/14] update: naming --- .../workflows/valence_band_offset.ipynb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb index bc48e62f..20792537 100644 --- a/other/materials_designer/workflows/valence_band_offset.ipynb +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -81,7 +81,7 @@ "\n", "# 3. Material parameters\n", "FOLDER = \"../uploads\"\n", - "INTERFACE_NAME = \"Interface\"\n", + "INTERFACE_NAME = \"CTa\"\n", "LEFT_SIDE_PART = InterfacePartsEnum.SUBSTRATE\n", "RIGHT_SIDE_PART = InterfacePartsEnum.FILM\n", "MATERIALS_SET_NAME = None # Defaults to the loaded interface name\n", @@ -152,7 +152,7 @@ "os.environ[\"API_PORT\"] = \"3000\"\n", "os.environ[\"API_SECURE\"] = \"false\"\n", "os.environ[\"API_HOST\"] = \"localhost\"\n", - "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"Q-9wDdNt-QJwA5Q6XAZTU1wNdFfoEambJEw1kBa7K9z\"" + "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"opaDA1kVKIkwCqC3QL--wmLNjn2HllIH1HA6Ni1iPNU\"" ] }, { @@ -290,13 +290,16 @@ "right_material = interface_get_part(interface, part=RIGHT_SIDE_PART)\n", "interface_material = interface.clone()\n", "\n", - "for material in [left_material, right_material]:\n", - " material.basis.set_labels_from_list([])\n", - " material.name = f\"{material.formula} Slab\"\n", + "left_material.basis.set_labels_from_list([])\n", + "right_material.basis.set_labels_from_list([])\n", "interface_material.basis.set_labels_from_list([])\n", + "\n", + "interface_part_names = interface_system_name.split(\", Interface\")[0].split(\"-\", 1)\n", + "left_material.name = f\"{interface_part_names[1]} Slab\"\n", + "right_material.name = f\"{interface_part_names[0]} Slab\"\n", "interface_material.name = interface_system_name\n", "\n", - "materials_by_role = {\"interface\": interface_material, \"left\": left_material, \"right\": right_material}\n", + "materials_by_role = {\"interface\": interface_material, \"substrate\": left_material, \"film\": right_material}\n", "for role, material in materials_by_role.items():\n", " print(f\" {role}: {material.name}\")\n", "visualize(list(materials_by_role.values()), repetitions=[1, 1, 1])\n" From 9477406916471d893ee1396ccd7314f8b417fed0 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Thu, 9 Apr 2026 13:09:39 -0700 Subject: [PATCH 09/14] update: order --- other/materials_designer/workflows/valence_band_offset.ipynb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb index 20792537..327691f4 100644 --- a/other/materials_designer/workflows/valence_band_offset.ipynb +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -152,7 +152,7 @@ "os.environ[\"API_PORT\"] = \"3000\"\n", "os.environ[\"API_SECURE\"] = \"false\"\n", "os.environ[\"API_HOST\"] = \"localhost\"\n", - "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"opaDA1kVKIkwCqC3QL--wmLNjn2HllIH1HA6Ni1iPNU\"" + "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"xxXp8UnwVONDGhNPCp-u3IkGL_loARb03HtfKe7ySKC\"" ] }, { @@ -302,7 +302,7 @@ "materials_by_role = {\"interface\": interface_material, \"substrate\": left_material, \"film\": right_material}\n", "for role, material in materials_by_role.items():\n", " print(f\" {role}: {material.name}\")\n", - "visualize(list(materials_by_role.values()), repetitions=[1, 1, 1])\n" + "visualize(list(materials_by_role.values()), repetitions=[1, 1, 1], rotation=\"-90x\")\n" ] }, { @@ -553,6 +553,7 @@ "from utils.visualize import display_JSON\n", "\n", "materials = list(saved_materials.values())\n", + "\n", "job_name = f\"{MY_WORKFLOW_NAME} {interface_system_name} {timestamp}\"\n", "workflow.name = job_name\n", "\n", From 8504fecc61991b739f30593cef9083de25881532 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Thu, 9 Apr 2026 14:23:03 -0700 Subject: [PATCH 10/14] update: use tags --- .../workflows/valence_band_offset.ipynb | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb index 327691f4..fe484ece 100644 --- a/other/materials_designer/workflows/valence_band_offset.ipynb +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -84,7 +84,7 @@ "INTERFACE_NAME = \"CTa\"\n", "LEFT_SIDE_PART = InterfacePartsEnum.SUBSTRATE\n", "RIGHT_SIDE_PART = InterfacePartsEnum.FILM\n", - "MATERIALS_SET_NAME = None # Defaults to the loaded interface name\n", + "INTERFACE_SYSTEM_NAME = None # Defaults to shorthand from the loaded interface name\n", "\n", "# 4. Workflow parameters\n", "APPLICATION_NAME = \"espresso\"\n", @@ -152,7 +152,7 @@ "os.environ[\"API_PORT\"] = \"3000\"\n", "os.environ[\"API_SECURE\"] = \"false\"\n", "os.environ[\"API_HOST\"] = \"localhost\"\n", - "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"xxXp8UnwVONDGhNPCp-u3IkGL_loARb03HtfKe7ySKC\"" + "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"EfDmUd-Q_J7Sk-LJYGUyImTR-hJHmH2OzSJjciD-Aju\"" ] }, { @@ -285,7 +285,8 @@ "source": [ "from mat3ra.made.tools.modify import interface_get_part\n", "\n", - "interface_system_name = MATERIALS_SET_NAME or interface.name\n", + "interface_shorthand = interface.name.split(\", Interface\")[0]\n", + "interface_system_name = INTERFACE_SYSTEM_NAME or interface_shorthand\n", "left_material = interface_get_part(interface, part=LEFT_SIDE_PART)\n", "right_material = interface_get_part(interface, part=RIGHT_SIDE_PART)\n", "interface_material = interface.clone()\n", @@ -294,10 +295,9 @@ "right_material.basis.set_labels_from_list([])\n", "interface_material.basis.set_labels_from_list([])\n", "\n", - "interface_part_names = interface_system_name.split(\", Interface\")[0].split(\"-\", 1)\n", - "left_material.name = f\"{interface_part_names[1]} Slab\"\n", - "right_material.name = f\"{interface_part_names[0]} Slab\"\n", - "interface_material.name = interface_system_name\n", + "interface_material.name = f\"{interface_system_name} Interface\"\n", + "left_material.name = f\"{interface_system_name} Left\"\n", + "right_material.name = f\"{interface_system_name} Right\"\n", "\n", "materials_by_role = {\"interface\": interface_material, \"substrate\": left_material, \"film\": right_material}\n", "for role, material in materials_by_role.items():\n", @@ -310,7 +310,7 @@ "id": "21", "metadata": {}, "source": [ - "### 3.3. Save materials and add to a materials set\n" + "### 3.3. Save materials\n" ] }, { @@ -321,20 +321,16 @@ "outputs": [], "source": [ "from mat3ra.made.material import Material\n", - "from utils.api import get_or_create_material\n", - "\n", - "existing = client.materials.list({\"name\": interface_system_name, \"owner._id\": ACCOUNT_ID})\n", - "materials_set = next((m for m in existing if m.get(\"isSet\")), None)\n", - "if materials_set is None:\n", - " materials_set = client.materials.create_set({\"name\": interface_system_name, \"owner\": {\"_id\": ACCOUNT_ID}})\n", - "print(f\"✅ Materials set: {materials_set['name']} ({materials_set['_id']})\")\n", "\n", "saved_materials = {}\n", "for role, material in materials_by_role.items():\n", - " saved = Material.create(get_or_create_material(client, material, ACCOUNT_ID))\n", - " client.materials.move_to_set(saved.id, \"\", materials_set[\"_id\"])\n", + " material_config = material.to_dict()\n", + " material_config[\"name\"] = material.name\n", + " existing_tags = material_config.get(\"tags\") or []\n", + " material_config[\"tags\"] = sorted(set([*existing_tags, interface_system_name]))\n", + " saved = Material.create(client.materials.create(material_config, owner_id=ACCOUNT_ID))\n", " saved_materials[role] = saved\n", - " print(f\" {role}: {saved.name} ({saved.id})\")\n" + " print(f\" {role}: {saved.name} ({saved.id}) | tags={material_config['tags']}\")\n" ] }, { @@ -601,7 +597,11 @@ "cell_type": "code", "execution_count": null, "id": "41", - "metadata": {}, + "metadata": { + "jupyter": { + "is_executing": true + } + }, "outputs": [], "source": [ "from utils.api import wait_for_jobs_to_finish_async\n", From 6878cb00976bc2b431e8286cdf257b14880fa4e5 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Thu, 9 Apr 2026 15:44:44 -0700 Subject: [PATCH 11/14] update: diag and mixing beta --- .../workflows/valence_band_offset.ipynb | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb index fe484ece..b6cede06 100644 --- a/other/materials_designer/workflows/valence_band_offset.ipynb +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -125,6 +125,10 @@ "SCF_KGRID = None # e.g. [8, 8, 1]\n", "KPATH = None # e.g. [{\"point\": \"G\", \"steps\": 20}, {\"point\": \"M\", \"steps\": 20}]\n", "\n", + "# SCF diagonalization and mixing\n", + "DIAGONALIZATION = \"david\" # \"david\" or \"cg\"\n", + "MIXING_BETA = 0.3\n", + "\n", "# Energy cutoffs\n", "ECUTWFC = 40\n", "ECUTRHO = 200\n" @@ -417,7 +421,7 @@ "id": "29", "metadata": {}, "source": [ - "### 4.4. Modify method (computational parameters): k-grid, k-path, and cutoffs\n" + "### 4.4. Modify method (computational parameters): k-grid, k-path, cutoffs, diagonalization, and mixing\n" ] }, { @@ -433,6 +437,18 @@ " PointsPathDataProvider,\n", ")\n", "\n", + "assert DIAGONALIZATION in {\"david\", \"cg\"}, \"DIAGONALIZATION must be 'david' or 'cg'\"\n", + "\n", + "\n", + "def set_pw_electrons_parameters(unit, diagonalization, mixing_beta):\n", + " unit.replace_in_input_content(r\"diagonalization\\s*=\\s*'[^']*'\", f\"diagonalization = '{diagonalization}'\")\n", + " unit.replace_in_input_content(r\"mixing_beta\\s*=\\s*[-+0-9.eE]+\", f\"mixing_beta = {mixing_beta}\")\n", + " for inp in unit.input:\n", + " if isinstance(inp, dict) and \"content\" in inp:\n", + " inp[\"rendered\"] = inp[\"content\"]\n", + " return unit\n", + "\n", + "\n", "for subworkflow in workflow.subworkflows:\n", " if subworkflow.application.name != APPLICATION_NAME:\n", " continue\n", @@ -457,6 +473,7 @@ " continue\n", " unit = subworkflow.get_unit_by_name(name=unit_name)\n", " unit.add_context(cutoffs_context)\n", + " unit = set_pw_electrons_parameters(unit, DIAGONALIZATION, MIXING_BETA)\n", " subworkflow.set_unit(unit)\n" ] }, From 7ff1ec451e03a2a7a291e2fcc53dc4a3eebe453f Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Thu, 9 Apr 2026 18:00:19 -0700 Subject: [PATCH 12/14] chore: cleanup --- .../workflows/valence_band_offset.ipynb | 105 +++++++----------- 1 file changed, 41 insertions(+), 64 deletions(-) diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb index b6cede06..00cddbeb 100644 --- a/other/materials_designer/workflows/valence_band_offset.ipynb +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -81,10 +81,10 @@ "\n", "# 3. Material parameters\n", "FOLDER = \"../uploads\"\n", - "INTERFACE_NAME = \"CTa\"\n", + "INTERFACE_NAME = \"Interface\" # To search for in \"uploads\" folder\n", "LEFT_SIDE_PART = InterfacePartsEnum.SUBSTRATE\n", "RIGHT_SIDE_PART = InterfacePartsEnum.FILM\n", - "INTERFACE_SYSTEM_NAME = None # Defaults to shorthand from the loaded interface name\n", + "INTERFACE_SYSTEM_NAME = None # Used as tag to group the materials. Defaults to shorthand from the loaded interface name\n", "\n", "# 4. Workflow parameters\n", "APPLICATION_NAME = \"espresso\"\n", @@ -150,21 +150,6 @@ "id": "8", "metadata": {}, "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"API_PORT\"] = \"3000\"\n", - "os.environ[\"API_SECURE\"] = \"false\"\n", - "os.environ[\"API_HOST\"] = \"localhost\"\n", - "os.environ[\"OIDC_ACCESS_TOKEN\"] = \"EfDmUd-Q_J7Sk-LJYGUyImTR-hJHmH2OzSJjciD-Aju\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9", - "metadata": {}, - "outputs": [], "source": [ "from utils.auth import authenticate\n", "\n", @@ -173,7 +158,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "### 2.2. Initialize API client\n" @@ -182,7 +167,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -194,7 +179,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "### 2.3. Select account\n" @@ -203,7 +188,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -213,7 +198,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -228,7 +213,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "14", "metadata": {}, "source": [ "### 2.4. Select project\n" @@ -237,7 +222,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -248,7 +233,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "16", "metadata": {}, "source": [ "## 3. Create materials\n", @@ -258,7 +243,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -273,7 +258,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "18", "metadata": {}, "source": [ "### 3.2. Create materials from interface parts\n", @@ -283,7 +268,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -311,7 +296,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "20", "metadata": {}, "source": [ "### 3.3. Save materials\n" @@ -320,7 +305,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -339,7 +324,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "22", "metadata": {}, "source": [ "## 4. Configure workflow\n", @@ -349,7 +334,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -363,7 +348,7 @@ }, { "cell_type": "markdown", - "id": "25", + "id": "24", "metadata": {}, "source": [ "### 4.2. Load workflow from Standata and preview it\n" @@ -372,7 +357,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -389,7 +374,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "26", "metadata": {}, "source": [ "### 4.3. Set model and its parameters (physics)\n" @@ -398,7 +383,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -418,7 +403,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "28", "metadata": {}, "source": [ "### 4.4. Modify method (computational parameters): k-grid, k-path, cutoffs, diagonalization, and mixing\n" @@ -427,7 +412,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -437,18 +422,14 @@ " PointsPathDataProvider,\n", ")\n", "\n", - "assert DIAGONALIZATION in {\"david\", \"cg\"}, \"DIAGONALIZATION must be 'david' or 'cg'\"\n", - "\n", - "\n", "def set_pw_electrons_parameters(unit, diagonalization, mixing_beta):\n", " unit.replace_in_input_content(r\"diagonalization\\s*=\\s*'[^']*'\", f\"diagonalization = '{diagonalization}'\")\n", " unit.replace_in_input_content(r\"mixing_beta\\s*=\\s*[-+0-9.eE]+\", f\"mixing_beta = {mixing_beta}\")\n", - " for inp in unit.input:\n", - " if isinstance(inp, dict) and \"content\" in inp:\n", - " inp[\"rendered\"] = inp[\"content\"]\n", + " for input in unit.input:\n", + " if isinstance(input, dict) and \"content\" in input:\n", + " input[\"rendered\"] = input[\"content\"]\n", " return unit\n", "\n", - "\n", "for subworkflow in workflow.subworkflows:\n", " if subworkflow.application.name != APPLICATION_NAME:\n", " continue\n", @@ -479,7 +460,7 @@ }, { "cell_type": "markdown", - "id": "31", + "id": "30", "metadata": {}, "source": [ "### 4.5. Preview final workflow\n" @@ -488,7 +469,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -497,7 +478,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "32", "metadata": {}, "source": [ "## 5. Create the compute configuration\n", @@ -507,7 +488,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -517,7 +498,7 @@ }, { "cell_type": "markdown", - "id": "35", + "id": "34", "metadata": {}, "source": [ "### 5.2. Create compute configuration\n" @@ -526,7 +507,7 @@ { "cell_type": "code", "execution_count": null, - "id": "36", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -547,7 +528,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "36", "metadata": {}, "source": [ "## 6. Create the job\n", @@ -557,7 +538,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "37", "metadata": {}, "outputs": [], "source": [ @@ -593,7 +574,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "38", "metadata": {}, "source": [ "## 7. Submit the job and monitor the status\n" @@ -602,7 +583,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -613,12 +594,8 @@ { "cell_type": "code", "execution_count": null, - "id": "41", - "metadata": { - "jupyter": { - "is_executing": true - } - }, + "id": "40", + "metadata": {}, "outputs": [], "source": [ "from utils.api import wait_for_jobs_to_finish_async\n", @@ -628,7 +605,7 @@ }, { "cell_type": "markdown", - "id": "42", + "id": "41", "metadata": {}, "source": [ "## 8. Retrieve and visualize results\n", @@ -638,7 +615,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "42", "metadata": {}, "outputs": [], "source": [ @@ -655,7 +632,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "43", "metadata": {}, "outputs": [], "source": [] From f42ea50cfebb00f11658abf4d211ee79d7c99abf Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Fri, 10 Apr 2026 10:57:38 -0700 Subject: [PATCH 13/14] update: show ESPs --- .../workflows/valence_band_offset.ipynb | 48 +++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/other/materials_designer/workflows/valence_band_offset.ipynb b/other/materials_designer/workflows/valence_band_offset.ipynb index 00cddbeb..09858eeb 100644 --- a/other/materials_designer/workflows/valence_band_offset.ipynb +++ b/other/materials_designer/workflows/valence_band_offset.ipynb @@ -21,7 +21,7 @@ "\n", "1. Set up the environment and parameters: install packages (JupyterLite only) and configure parameters for the interface, workflow, compute resources, and job.\n", "1. Authenticate and initialize API client: authenticate via browser, initialize the client, then select account and project.\n", - "1. Create materials: load an interface from the `../uploads` folder, split it into interface/left/right parts using interface labels, strip labels required by Quantum ESPRESSO, and save all three materials to the platform in a materials set.\n", + "1. Create materials: load an interface from the `../uploads` folder, split it into interface/left/right parts using interface labels, strip labels required by Quantum ESPRESSO, and save all three materials to the platform tagged with the interface name.\n", "1. Configure workflow: select application, load the VBO workflow from Standata, assign materials to subworkflows by role, set model and computational parameters, and preview the workflow.\n", "1. Configure compute: get list of clusters and create compute configuration with selected cluster, queue, and number of processors.\n", "1. Create the job with materials and workflow configuration: assemble the job from materials, workflow, project, and compute configuration.\n", @@ -62,7 +62,8 @@ "id": "3", "metadata": {}, "source": [ - "### 1.2. Set parameters\n" + "### 1.2. Set parameters\n", + "Provide the INTERFACE_NAME to match the name of the structure in \"uploads\" folder for search. Left and right parts will be extracted based on substrate/film labels present in the generated interface." ] }, { @@ -88,7 +89,7 @@ "\n", "# 4. Workflow parameters\n", "APPLICATION_NAME = \"espresso\"\n", - "WORKFLOW_SEARCH_TERM = \"valence_band_offset\"\n", + "WORKFLOW_SEARCH_TERM = \"valence_band_offset.json\"\n", "MY_WORKFLOW_NAME = \"VBO\"\n", "\n", "# 5. Compute parameters\n", @@ -272,9 +273,11 @@ "metadata": {}, "outputs": [], "source": [ + "import re\n", "from mat3ra.made.tools.modify import interface_get_part\n", "\n", - "interface_shorthand = interface.name.split(\", Interface\")[0]\n", + "interface_shorthand = re.match(r\"^(.+?)\\s\", interface.name).group(1) if re.match(r\"^(.+?)\\s\",\n", + " interface.name) else INTERFACE_NAME\n", "interface_system_name = INTERFACE_SYSTEM_NAME or interface_shorthand\n", "left_material = interface_get_part(interface, part=LEFT_SIDE_PART)\n", "right_material = interface_get_part(interface, part=RIGHT_SIDE_PART)\n", @@ -422,6 +425,7 @@ " PointsPathDataProvider,\n", ")\n", "\n", + "\n", "def set_pw_electrons_parameters(unit, diagonalization, mixing_beta):\n", " unit.replace_in_input_content(r\"diagonalization\\s*=\\s*'[^']*'\", f\"diagonalization = '{diagonalization}'\")\n", " unit.replace_in_input_content(r\"mixing_beta\\s*=\\s*[-+0-9.eE]+\", f\"mixing_beta = {mixing_beta}\")\n", @@ -430,6 +434,7 @@ " input[\"rendered\"] = input[\"content\"]\n", " return unit\n", "\n", + "\n", "for subworkflow in workflow.subworkflows:\n", " if subworkflow.application.name != APPLICATION_NAME:\n", " continue\n", @@ -609,7 +614,7 @@ "metadata": {}, "source": [ "## 8. Retrieve and visualize results\n", - "### 8.1. Valence Band Offset\n" + "### 8.1. Valence Band Offset and average ESP profiles\n" ] }, { @@ -622,11 +627,38 @@ "from mat3ra.prode import PropertyName\n", "from utils.visualize import visualize_properties\n", "\n", - "vbo_value = client.properties.get_for_job(job_id, property_name=PropertyName.scalar.valence_band_offset.value)[0]\n", + "job_data = client.jobs.get(job_id)\n", + "workflow = job_data[\"workflow\"]\n", + "\n", + "vbo_value = client.properties.get_for_job(\n", + " job_id,\n", + " property_name=PropertyName.scalar.valence_band_offset.value,\n", + ")[0]\n", "print(f\"Valence Band Offset (VBO) value: {vbo_value['value']:.3f} eV\")\n", "\n", - "vbo_data = client.properties.get_for_job(job_id)\n", - "visualize_properties(vbo_data, title=\"Valence Band Offset\")" + "avg_esp_unit_ids = {}\n", + "for subworkflow in workflow[\"subworkflows\"]:\n", + " subworkflow_name = subworkflow[\"name\"]\n", + " for unit in subworkflow[\"units\"]:\n", + " result_names = [result[\"name\"] for result in unit.get(\"results\", [])]\n", + " if \"average_potential_profile\" in result_names:\n", + " avg_esp_unit_ids[subworkflow_name] = unit[\"flowchartId\"]\n", + " break\n", + "\n", + "ordered_names = [\n", + " \"BS + Avg ESP (Interface)\",\n", + " \"BS + Avg ESP (interface left)\",\n", + " \"BS + Avg ESP (interface right)\",\n", + "]\n", + "\n", + "for subworkflow_name in ordered_names:\n", + " unit_id = avg_esp_unit_ids[subworkflow_name]\n", + " avg_esp_data = client.properties.get_for_job(\n", + " job_id,\n", + " property_name=\"average_potential_profile\",\n", + " unit_id=unit_id,\n", + " )[0]\n", + " visualize_properties(avg_esp_data, title=subworkflow_name)\n" ] }, { From 30115ba785d2edbc1929f4ac9b67f4c96b09dfa2 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Fri, 10 Apr 2026 10:59:43 -0700 Subject: [PATCH 14/14] update: intro nb --- other/materials_designer/workflows/Introduction.ipynb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/other/materials_designer/workflows/Introduction.ipynb b/other/materials_designer/workflows/Introduction.ipynb index 5f02082e..6fa6699d 100644 --- a/other/materials_designer/workflows/Introduction.ipynb +++ b/other/materials_designer/workflows/Introduction.ipynb @@ -87,7 +87,7 @@ "## 8. Electronics\n", "\n", "### 8.1. Valence Band Offset\n", - "#### 8.1.1. Valence band offset at an interface. *(to be added)*\n", + "#### [8.1.1. Valence band offset at an interface.](valence_band_offset.ipynb)\n", "\n", "### 8.2. Dielectric Tensor\n", "#### 8.2.1. Dielectric tensor calculation. *(to be added)*\n", @@ -98,6 +98,14 @@ "### 9.1. Python / Shell\n", "#### 9.1.1. Custom Python and Shell workflows. *(to be added)*\n" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": {