diff --git a/other/materials_designer/workflows/run_bandgap_workflow_max.ipynb b/other/materials_designer/workflows/run_bandgap_workflow_max.ipynb new file mode 100644 index 00000000..eb43d4e0 --- /dev/null +++ b/other/materials_designer/workflows/run_bandgap_workflow_max.ipynb @@ -0,0 +1,413 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Bandgap Workflow Example\n", + " This notebook demonstrates how to build and run a bandgap workflow for a material.\n", + " Example of building and running a bandgap workflow for twisted MoS2 interface from specific_examples.\n", + "\n", + "## Process Overview\n", + "### 1. Set up the environment and parameters.\n", + "### 2. Log in to get the API token\n", + "### 3. Load the target material.\n", + "### 4. Import workflow builder and related analyzers.\n", + "### 5. Analyze material to get parameters for the workflow configuration.\n", + "### 6. Create the workflow configuration.\n", + "### 7. Create a job with material and workflow configuration.\n", + "### 8. Submit the job to the server.\n", + "### 9. Monitor the job status and retrieve results.\n", + "### 10. Display the results." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## 1. Set up the environment and parameters" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## 2. Log in to get the API token" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.api import ApiClient\n", + "\n", + "# Log in to get the API token\n", + "auth_config = await ApiClient().login()" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## 3. Load the target material" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "from utils.visualize import visualize_materials as visualize\n", + "from utils.jupyterlite import load_material_from_folder\n", + "\n", + "material = load_material_from_folder(\"/uploads\", \"MoS2_twisted_interface_60_degrees.json\")\n", + "visualize(material)" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "## 5. Create workflow and set its parameters\n", + "### 5.1. Get list of applications and select one" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.standata.applications import ApplicationStandata\n", + "\n", + "# Get Applications list (with versions, build)\n", + "apps_list = ApplicationStandata.list_all()\n", + "# returns apps_list[0] = [{\"name\" : \"espresso\", \"version\": \"7.2\", \"build\": \"GNU\"}]\n", + "\n", + "app = ApplicationStandata.get_by_name_first_match(\"espresso\")\n", + "# returns name, version, build config" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "### 5.2. Create workflow from standard workflows and preview it" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "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", + "# Search WF by name and application\n", + "workflow_config = WorkflowStandata.filter_by_application(app).get_by_name_first_match(\"band_gap\")\n", + "workflow = Workflow.create(workflow_config)\n", + "\n", + "# View workflow to understand its structure\n", + "visualize_workflow(workflow)" + ] + }, + { + "cell_type": "markdown", + "id": "10", + "metadata": {}, + "source": [ + "### 5.3. Add relaxation subworkflow" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "workflow.add_relaxation()\n", + "visualize_workflow(workflow)\n", + "# Relaxation subworkflow is added as the first subworkflow" + ] + }, + { + "cell_type": "markdown", + "id": "12", + "metadata": {}, + "source": [ + "### 5.4. Change subworkflow details (Model subtype)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.standata.model_tree import ModelTreeStandata\n", + "\n", + "swf_0 = workflow.subworkflows[0] # relaxation subworkflow\n", + "swf_1 = workflow.subworkflows[1] # band structure subworkflow\n", + "\n", + "# Change model subtype for relaxation subworkflow\n", + "# For preview:\n", + "subtypes = ModelTreeStandata.get_subtypes_by_model_type(\"dft\") # [\"gga\", \"lda\"] as enum\n", + "functionals = ModelTreeStandata.get_functionals_by_subtype(\"dft\", subtypes.LDA) # [\"pz\", ...] as enum\n", + "\n", + "model = ModelTreeStandata.get_model_by_parameters(\n", + " model_type=\"dft\",\n", + " subtype=subtypes.LDA,\n", + " functional=functionals.PZ,\n", + ")\n", + "swf_0.model = model\n", + "swf_1.model = model" + ] + }, + { + "cell_type": "markdown", + "id": "14", + "metadata": {}, + "source": [ + "### 5.5. Modify k-grid in subworkflow units" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.wode.context_providers import PointsGridFormDataProvider\n", + "\n", + "# Values from publication\n", + "kgrid_scf = [6, 6, 1]\n", + "kgrid_nscf = [12, 12, 1]\n", + "kgrid_relax = kgrid_scf\n", + "\n", + "# TODO: helper to workflow.set_context_to_unit_by_name_regex(context_provider, unit_name_regex)\n", + "kgrid_context_provider = PointsGridFormDataProvider(material=material)\n", + "\n", + "new_context_relax = kgrid_context_provider.get_data(dimensions=kgrid_relax)\n", + "new_context_scf = kgrid_context_provider.get_data(dimensions=kgrid_scf)\n", + "new_context_nscf = kgrid_context_provider.get_data(dimensions=kgrid_nscf)\n", + "\n", + "# Get workflow's specific unit that needs to be modified\n", + "# Option A-1: search is done by unit name regex across the entire workflow\n", + "unit_to_modify_relax = workflow.get_unit_by_name(name_regex=\"relax\")\n", + "unit_to_modify_relax.context.add_context(new_context_relax)\n", + "\n", + "# Option A-2: search is done by unit name within a specific subworkflow\n", + "unit_to_modify_scf = workflow.subworkflows[1].get_unit_by_name(name=\"pw_scf\")\n", + "unit_to_modify_scf.context.add_context(new_context_scf)\n", + "\n", + "# Set the modified unit back to the workflow\n", + "# Option B-1: direct set by unit object, replacing the existing one\n", + "workflow.set_unit(unit_to_modify_relax)\n", + "\n", + "# Option B-2: set by unit flowchart id and new unit object\n", + "workflow.set_unit(unit_flowchart_id=unit_to_modify_scf.flowchart_id, new_unit=unit_to_modify_scf)\n", + "\n", + "# Option B-3: set context to unit directly\n", + "workflow.get_unit_by_name(name=\"pw_nscf\").set_context(new_context_nscf)\n", + "# workflow.set_context_to_unit(unit_name=\"pw_nscf\", new_context=new_context_nscf)" + ] + }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "## 6. Create the compute configuration\n", + "### 6.1. View available clusters and providers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "# List available compute providers and clusters\n", + "from mat3ra.ide.compute import ComputeProvider, ComputeCluster\n", + "\n", + "providers = ComputeProvider.list_all()\n", + "clusters = ComputeCluster.list_all()" + ] + }, + { + "cell_type": "markdown", + "id": "18", + "metadata": {}, + "source": [ + "### 6.2. Create compute configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.ide.compute import ComputeConfiguration, QueueTypesEnum\n", + "\n", + "compute_config = ComputeConfiguration(\n", + " queue=QueueTypesEnum.OR8,\n", + " nodes=1,\n", + " ppn=8,\n", + " cluster=clusters[0], # select first available cluster\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "## 7. Create the job with material and workflow configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.jode.job import create_job\n", + "\n", + "job = create_job(\n", + " workflow=workflow,\n", + " material=material,\n", + " compute=compute_config,\n", + " auth_config=auth_config\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "id": "22", + "metadata": {}, + "source": [ + "## 8. Submit the job and monitor the status" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.prode import PropertyEnum\n", + "\n", + "job.run()\n", + "job.wait_for_complete()\n", + "# job.check_status()\n", + "# job.get_current_output()" + ] + }, + { + "cell_type": "markdown", + "id": "24", + "metadata": {}, + "source": [ + "## 9. Retrieve results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "# AFTER Finished\n", + "# A class from Prode to handle results\n", + "results = job.get_results(PropertyEnum.BAND_GAP, PropertyEnum.BAND_STRUCTURE)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26", + "metadata": {}, + "outputs": [], + "source": [ + "# Prode has property analyzer to get all eigen_values and return occupied and unoccupied states\n", + "# Gets data from express that has parser" + ] + }, + { + "cell_type": "markdown", + "id": "27", + "metadata": {}, + "source": [ + "## 10. Display results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "# Visual library that can visualize any property defined in Prode\n", + "# Use similar to Wave\n", + "from mat3ra.prove import visualize_property\n", + "\n", + "visualize_property(results.band_structure)\n", + "print(results.band_gap)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/other/materials_designer/workflows/run_bandgap_workflow_min.ipynb b/other/materials_designer/workflows/run_bandgap_workflow_min.ipynb new file mode 100644 index 00000000..7b50747a --- /dev/null +++ b/other/materials_designer/workflows/run_bandgap_workflow_min.ipynb @@ -0,0 +1,622 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Bandgap Workflow Example\n", + " This notebook demonstrates how to build and run a bandgap workflow for a material.\n", + " Example of building and running a bandgap workflow for twisted MoS2 interface from specific_examples.\n", + "\n", + "## Process Overview\n", + "### 1. Set up the environment and parameters.\n", + "### 2. Log in to get the API token\n", + "### 3. Load the target material.\n", + "### 4. Import workflow builder and related analyzers.\n", + "### 5. Analyze material to get parameters for the workflow configuration.\n", + "### 6. Create the workflow configuration.\n", + "### 7. Create a job with material and workflow configuration.\n", + "### 8. Submit the job to the server.\n", + "### 9. Monitor the job status and retrieve results.\n", + "### 10. Display the results." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## 1. Set up the environment and parameters" + ] + }, + { + "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\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "from types import SimpleNamespace\n", + "\n", + "# TODO: move to AX utils\n", + "# Helper function to convert dictionaries to SimpleNamespace objects for dot notation access\n", + "def dict_to_namespace(obj):\n", + " if isinstance(obj, dict):\n", + " return SimpleNamespace(**{k: dict_to_namespace(v) for k, v in obj.items()})\n", + " elif isinstance(obj, list):\n", + " return [dict_to_namespace(item) for item in obj]\n", + " else:\n", + " return obj" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## 2. Log in to get the API token" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "ACCOUNT_ID = \"ACCOUNT_ID\"\n", + "AUTH_TOKEN = \"AUTH_TOKEN\"\n", + "ORGANIZATION_ID = \"ORGANIZATION_ID\"\n", + "\n", + "import os\n", + "import sys\n", + "import json\n", + "\n", + "if sys.platform == \"emscripten\":\n", + " # Only works if launched within the Platform, otherwise requires redirect to Login\n", + " apiConfig = data_from_host.get(\"apiConfig\")\n", + " os.environ.update(data_from_host.get(\"environ\", {}))\n", + " os.environ.update(\n", + " dict(\n", + " ACCOUNT_ID=apiConfig.get(\"accountId\"),\n", + " AUTH_TOKEN=apiConfig.get(\"authToken\"),\n", + " ORGANIZATION_ID=apiConfig.get(\"organizationId\", \"\"),\n", + " CLUSTERS=json.dumps(apiConfig.get(\"clusters\", [])),\n", + " )\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "## 3. Create material\n", + "### 3.1. Load material from local file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "from utils.visualize import visualize_materials as visualize\n", + "from utils.jupyterlite import load_material_from_folder\n", + "\n", + "material = load_material_from_folder(\"/uploads\", \"MoS2_twisted_interface_60_degrees.json\")\n", + "visualize(material)" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "### 3.2. Save material to the platform" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "from utils.settings import ENDPOINT_ARGS, ACCOUNT_ID\n", + "from utils.generic import display_JSON\n", + "from exabyte_api_client.endpoints.materials import MaterialEndpoints\n", + "\n", + "\n", + "\n", + "OWNER_ID = os.getenv(\"ORGANIZATION_ID\") or ACCOUNT_ID\n", + "endpoint = MaterialEndpoints(*ENDPOINT_ARGS)\n", + "\n", + "RAW_CONFIG = material.to_dict()\n", + "fields = [\"name\", \"lattice\", \"basis\"]\n", + "CONFIG = {key: RAW_CONFIG[key] for key in fields}\n", + "\n", + "saved_material_response = endpoint.create(CONFIG, OWNER_ID)\n", + "saved_material = dict_to_namespace(saved_material_response)" + ] + }, + { + "cell_type": "markdown", + "id": "10", + "metadata": {}, + "source": [ + "### 3.3. Get material id" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "material_id = saved_material.get(\"_id\")\n", + "print(\"Material IDs:\", material_id)" + ] + }, + { + "cell_type": "markdown", + "id": "12", + "metadata": {}, + "source": [ + "## 5. Create workflow and set its parameters\n", + "### 5.1. Get list of applications and select one" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.standata.applications import ApplicationStandata\n", + "from mat3ra.ade.application import Application\n", + "\n", + "# Get Applications list (with versions, build)\n", + "apps_list = ApplicationStandata.list_all()\n", + "# returns apps_list[0] = [{\"name\" : \"espresso\", \"version\": \"7.2\", \"build\": \"GNU\"}]\n", + "\n", + "app_config = ApplicationStandata.get_by_name_first_match(\"espresso\")\n", + "# returns name, version, build config\n", + "app=Application(**app_config)" + ] + }, + { + "cell_type": "markdown", + "id": "14", + "metadata": {}, + "source": [ + "### 5.2. Create workflow from standard workflows and preview it" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "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", + "# Search WF by name and application\n", + "workflow_config = WorkflowStandata.filter_by_application(app.name).get_by_name_first_match(\"band_gap\")\n", + "workflow = Workflow.create(workflow_config)\n", + "\n", + "# View workflow to understand its structure\n", + "# workflow\n", + "visualize_workflow(workflow)" + ] + }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "### 5.3. Add relaxation subworkflow" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "workflow.add_relaxation()\n", + "visualize_workflow(workflow)\n", + "# Relaxation subworkflow is added as the first subworkflow" + ] + }, + { + "cell_type": "markdown", + "id": "18", + "metadata": {}, + "source": [ + "### 5.4. Change subworkflow details (Model subtype)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.standata.model_tree import ModelTreeStandata\n", + "\n", + "swf_0 = workflow.subworkflows[0] # relaxation subworkflow\n", + "swf_1 = workflow.subworkflows[1] # band structure subworkflow\n", + "\n", + "# Change model subtype for relaxation subworkflow\n", + "# For preview:\n", + "subtypes = ModelTreeStandata.get_subtypes_by_model_type(\"dft\") # [\"gga\", \"lda\"] as enum\n", + "functionals = ModelTreeStandata.get_functionals_by_subtype(\"dft\", subtypes.LDA) # [\"pz\", ...] as enum\n", + "\n", + "model = ModelTreeStandata.get_model_by_parameters(\n", + " model_type=\"dft\",\n", + " subtype=subtypes.LDA,\n", + " functional=functionals.PZ,\n", + ")\n", + "swf_0.model = model\n", + "swf_1.model = model" + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "### 5.5. Modify k-grid in subworkflow units" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.wode.context_providers import PointsGridFormDataProvider\n", + "\n", + "# Values from publication\n", + "kgrid_scf = [6, 6, 1]\n", + "kgrid_nscf = [12, 12, 1]\n", + "kgrid_relax = kgrid_scf\n", + "\n", + "# In future: helper to workflow.set_context_to_unit_by_name_regex(context_provider, unit_name_regex)\n", + "kgrid_context_provider = PointsGridFormDataProvider(material=material)\n", + "\n", + "new_context_relax = kgrid_context_provider.get_data(dimensions=kgrid_relax)\n", + "new_context_scf = kgrid_context_provider.get_data(dimensions=kgrid_scf)\n", + "new_context_nscf = kgrid_context_provider.get_data(dimensions=kgrid_nscf)\n", + "\n", + "# Get workflow's specific unit that needs to be modified\n", + "# Option 1: search is done by unit name regex across the entire workflow\n", + "unit_to_modify_relax = workflow.get_unit_by_name(name_regex=\"relax\")\n", + "unit_to_modify_relax.context.add_context(new_context_relax)\n", + "\n", + "# Option 2: search is done by unit name within a specific subworkflow\n", + "unit_to_modify_scf = workflow.subworkflows[1].get_unit_by_name(name=\"pw_scf\")\n", + "unit_to_modify_scf.context.add_context(new_context_scf)\n", + "unit_to_modify_nscf = workflow.subworkflows[1].get_unit_by_name(name=\"pw_nscf\")\n", + "unit_to_modify_nscf.context.add_context(new_context_nscf)\n", + "\n", + "# Set the modified unit back to the workflow\n", + "# Option 1: direct set by unit object, replacing the existing one\n", + "workflow.set_unit(unit_to_modify_relax)\n", + "\n", + "# Option 2: set by unit flowchart id and new unit object\n", + "workflow.set_unit(unit_flowchart_id=unit_to_modify_scf.flowchart_id, new_unit=unit_to_modify_scf)\n", + "workflow.set_unit(unit_flowchart_id=unit_to_modify_nscf.flowchart_id, new_unit=unit_to_modify_nscf)\n" + ] + }, + { + "cell_type": "markdown", + "id": "22", + "metadata": {}, + "source": [ + "### 5.6. Save workflow to collection" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "metadata": {}, + "outputs": [], + "source": [ + "from exabyte_api_client import WorkflowEndpoints\n", + "from utils.settings import ENDPOINT_ARGS, ACCOUNT_ID\n", + "\n", + "workflow_endpoints = WorkflowEndpoints(*ENDPOINT_ARGS)\n", + "\n", + "saved_workflow_data = workflow_endpoints.create(config=workflow.to_dict(), owner_id=ACCOUNT_ID)" + ] + }, + { + "cell_type": "markdown", + "id": "24", + "metadata": {}, + "source": [ + "## 6. Create the compute configuration\n", + "### 6.1. View available clusters and providers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "cluster_config = next(iter(json.loads(os.getenv(\"CLUSTERS\"))), {})\n", + "queue_configs = cluster_config.get(\"queues\", [])\n", + "\n", + "CLUSTER_NAME = cluster_config.get(\"displayName\", \"cluster-001\")" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, + "source": [ + "### 6.2. Create compute configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27", + "metadata": {}, + "outputs": [], + "source": [ + "from exabyte_api_client.endpoints.jobs import JobEndpoints\n", + "\n", + "exabyte_jobs_endpoint = JobEndpoints(*ENDPOINT_ARGS)\n", + "\n", + "compute = exabyte_jobs_endpoint.get_compute(\n", + " ppn=8,\n", + " queue=\"OR8\",\n", + " nodes=1,\n", + " time_limit=\"00:30:00\",\n", + " cluster=CLUSTER_NAME\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "28", + "metadata": {}, + "source": [ + "## 7. Create the job with material and workflow configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29", + "metadata": {}, + "outputs": [], + "source": [ + "from exabyte_api_client.endpoints.materials import MaterialEndpoints\n", + "from exabyte_api_client.endpoints.projects import ProjectEndpoints\n", + "\n", + "from datetime import datetime\n", + "\n", + "# Add helper to AX utils: create_material(material, owner_id: Optional)\n", + "material_endpoints = MaterialEndpoints(*ENDPOINT_ARGS)\n", + "OWNER_ID = os.getenv(\"ORGANIZATION_ID\") or ACCOUNT_ID\n", + "\n", + "# Add helper to AX utils: create_workflow(workflow, owner_id: Optional)\n", + "project_endpoints = ProjectEndpoints(*ENDPOINT_ARGS)\n", + "project_id = project_endpoints.list({\"isDefault\": True, \"owner._id\": OWNER_ID})[0][\"_id\"]\n", + "\n", + "timestamp = datetime.now().strftime(\"%Y-%m-%d %H:%M\") # for name to be found easily\n", + "\n", + "JOB_NAME = f\"Band Gap {timestamp}\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30", + "metadata": {}, + "outputs": [], + "source": [ + "job_endpoints = JobEndpoints(*ENDPOINT_ARGS)\n", + "\n", + "jobs_response = job_endpoints.create_by_ids(\n", + " materials=material,\n", + " workflow_id=workflow[\"_id\"],\n", + " project_id=project_id,\n", + " prefix=JOB_NAME,\n", + " owner_id=OWNER_ID,\n", + " compute=compute\n", + ")\n", + "\n", + "# Convert jobs list to SimpleNamespace objects for dot notation access\n", + "jobs = dict_to_namespace(jobs_response)\n", + "\n", + "display_JSON(jobs_response)" + ] + }, + { + "cell_type": "markdown", + "id": "31", + "metadata": {}, + "source": [ + "## 8. Submit the job and monitor the status" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32", + "metadata": {}, + "outputs": [], + "source": [ + "# Submit each job (assuming jobs is a list of job objects)\n", + "if isinstance(jobs, list):\n", + " for job in jobs:\n", + " job_endpoints.submit(job._id)\n", + "else:\n", + " # If jobs is a single job object\n", + " job_endpoints.submit(jobs._id)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33", + "metadata": {}, + "outputs": [], + "source": [ + "from utils.generic import wait_for_jobs_to_finish\n", + "\n", + "# Extract job IDs using dot notation\n", + "if isinstance(jobs, list):\n", + " job_ids = [job._id for job in jobs]\n", + "else:\n", + " job_ids = [jobs._id]\n", + "\n", + "wait_for_jobs_to_finish(job_endpoints, job_ids, poll_interval=60)" + ] + }, + { + "cell_type": "markdown", + "id": "34", + "metadata": {}, + "source": [ + "## 9. Retrieve results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35", + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "from utils.generic import get_property_by_subworkflow_and_unit_indicies\n", + "from exabyte_api_client.endpoints.properties import PropertiesEndpoints\n", + "\n", + "property_endpoints = PropertiesEndpoints(*ENDPOINT_ARGS)\n", + "\n", + "results = []\n", + "# Iterate in general, but we have only 1 material here\n", + "for material in [saved_material]:\n", + " # Find the job that matches this material - fix typo: _meterial -> _material\n", + " job = next((job for job in jobs if job._material._id == material._id))\n", + " final_structure = get_property_by_subworkflow_and_unit_indicies(property_endpoints, \"final_structure\", job, 0, 0)[\n", + " \"data\"\n", + " ]\n", + " pressure = get_property_by_subworkflow_and_unit_indicies(property_endpoints, \"pressure\", job, 0, 0)[\"data\"][\"value\"]\n", + "\n", + " # Use dot notation to access workflow property\n", + " unit_flowchart_id = Workflow(job.workflow).get_unit_by_name(name=\"pw_scf\").flowchart_id\n", + " band_gap_direct = property_endpoints.get_direct_band_gap(job._id, unit_flowchart_id)\n", + " band_gap_indirect = property_endpoints.get_indirect_band_gap(job._id, unit_flowchart_id)\n", + "\n", + " results.append(\n", + " {\n", + " \"material_id\": material._id,\n", + " \"angle_deg\": re.search(r\"(\\d+(?:\\.\\d+)?) degrees\", material.name).group(1),\n", + " \"band_gap_direct\": band_gap_direct,\n", + " \"band_gap_indirect\": band_gap_indirect,\n", + " }\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "36", + "metadata": {}, + "source": [ + "## 10. Display results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib import pyplot as plt\n", + "import pandas as pd\n", + "\n", + "df = pd.DataFrame(results).dropna(subset=[\"band_gap_direct\", \"band_gap_indirect\"]).sort_values(\"angle_deg\")\n", + "display(df)\n", + "\n", + "plt.figure(figsize=(5, 3.6), dpi=130)\n", + "plt.scatter(df[\"angle_deg\"], df[\"band_gap_direct\"], marker=\">\", label=\"K-valley bandgap (direct)\")\n", + "plt.scatter(df[\"angle_deg\"], df[\"band_gap_indirect\"], marker=\"<\", label=\"Indirect bandgap\")\n", + "plt.xlabel(r\"$\\theta$ (°)\")\n", + "plt.ylabel(\"Energy (eV)\")\n", + "plt.xlim(-2, 62)\n", + "plt.legend(frameon=False, loc=\"best\")\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}