From 8939727fc594f7e562e6d2706363c709b730411c Mon Sep 17 00:00:00 2001 From: Pranab Das <31024886+pranabdas@users.noreply.github.com> Date: Wed, 5 Jul 2023 22:04:39 +0800 Subject: [PATCH] update notebook with working example --- .gitattributes | 2 + examples/workflow/qe_scf_calculation.ipynb | 365 ++++++++++++++++----- examples/workflow/qe_scf_calculation.py | 282 ++++++++++++---- 3 files changed, 517 insertions(+), 132 deletions(-) diff --git a/.gitattributes b/.gitattributes index f2555d0f..6a3c384d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,3 +13,5 @@ examples/material/import_materials_from_poscar.ipynb !filter !diff !merge tex examples/system/get_authentication_params.ipynb !filter !diff !merge text examples/workflow/get_workflows.ipynb !filter !diff !merge text images/*.png filter=lfs diff=lfs merge=lfs -text +*.upf filter=lfs diff=lfs merge=lfs -text +examples/assets/bash_workflow_template.json filter=lfs diff=lfs merge=lfs -text diff --git a/examples/workflow/qe_scf_calculation.ipynb b/examples/workflow/qe_scf_calculation.ipynb index 948dfa66..ade2d192 100644 --- a/examples/workflow/qe_scf_calculation.ipynb +++ b/examples/workflow/qe_scf_calculation.ipynb @@ -17,7 +17,7 @@ "id": "92673fc2-8999-415b-a866-7692a3e7d682", "metadata": {}, "source": [ - "# Quantum Espresso SCF calcuation via API\n" + "# Quantum Espresso SCF calculation via API\n" ] }, { @@ -71,86 +71,162 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "504fd5dd-5411-4073-be50-62dd84baeb9b", "metadata": {}, "outputs": [], "source": [ "from utils.settings import ENDPOINT_ARGS, ACCOUNT_ID\n", - "from utils.generic import display_JSON\n", + "from utils.generic import display_JSON, wait_for_jobs_to_finish\n", "\n", - "from exabyte_api_client.endpoints.workflows import WorkflowEndpoints" + "from exabyte_api_client.endpoints.workflows import WorkflowEndpoints\n", + "from exabyte_api_client.endpoints.materials import MaterialEndpoints\n", + "from exabyte_api_client.endpoints.jobs import JobEndpoints" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "b4e2b1f6-40be-4a7e-aa49-1fa36df4e33c", "metadata": {}, "outputs": [], "source": [ "# Initialize a helper class to interact with WorkflowEndpoints\n", - "endpoint = WorkflowEndpoints(*ENDPOINT_ARGS)" + "workflow_endpoints = WorkflowEndpoints(*ENDPOINT_ARGS)\n", + "material_endpoints = MaterialEndpoints(*ENDPOINT_ARGS)\n", + "job_endpoints = JobEndpoints(*ENDPOINT_ARGS)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d15bb26d", + "metadata": {}, + "source": [ + "#### Create a Quantum Espresso workflow for SCF calculation" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "d4d3c4c1-04cb-46a8-999e-fb2d54786994", "metadata": {}, "outputs": [], "source": [ "# payload for workflow creation\n", - "BODY = {\n", - " \"name\": \"Silicon-SCF-bash-REST\",\n", + "WORKFLOW_BODY = {\n", + " \"name\": \"Silicon SCF\",\n", " \"subworkflows\": [\n", " {\n", - " \"name\": \"QE-SCF\",\n", + " \"name\": \"Total energy\",\n", " \"application\": {\n", - " \"name\": \"shell\",\n", - " \"version\": \"4.2.46\",\n", - " \"build\": \"Default\",\n", - " \"isDefault\": \"true\",\n", - " \"summary\": \"Shell Script\",\n", - " \"shortName\": \"sh\",\n", + " \"_id\": \"u2qtBhseFfQMWRYND\",\n", + " \"name\": \"espresso\",\n", + " \"shortName\": \"qe\",\n", + " \"summary\": \"Quantum Espresso\",\n", + " \"build\": \"Intel\",\n", + " \"version\": \"6.3\",\n", + " \"isDefault\": False,\n", + " \"hasAdvancedComputeOptions\": True,\n", + " \"schemaVersion\": \"2022.8.16\",\n", + " \"createdAt\": \"2023-04-13T09:42:52.622Z\",\n", + " \"createdBy\": \"0\",\n", + " \"updatedAt\": \"2023-06-15T20:01:39.744Z\",\n", + " \"updatedBy\": \"0\",\n", + " },\n", + " \"properties\": [\n", + " \"total_energy\",\n", + " \"total_energy_contributions\",\n", + " \"pressure\",\n", + " \"fermi_energy\",\n", + " \"atomic_forces\",\n", + " \"total_force\",\n", + " \"stress_tensor\",\n", + " ],\n", + " \"model\": {\n", + " \"type\": \"dft\",\n", + " \"subtype\": \"gga\",\n", + " \"method\": {\"type\": \"pseudopotential\", \"subtype\": \"us\", \"data\": {\"searchText\": \"\"}},\n", + " \"functional\": {\"slug\": \"pbe\"},\n", + " \"refiners\": [],\n", + " \"modifiers\": [],\n", " },\n", - " \"properties\": [\"total_energy\", \"fermi_energy\"],\n", " \"units\": [\n", " {\n", " \"type\": \"execution\",\n", " \"application\": {\n", - " \"name\": \"shell\",\n", - " \"version\": \"4.2.46\",\n", - " \"build\": \"Default\",\n", - " \"isDefault\": \"true\",\n", - " \"summary\": \"Shell Script\",\n", - " \"shortName\": \"sh\",\n", + " \"_id\": \"u2qtBhseFfQMWRYND\",\n", + " \"name\": \"espresso\",\n", + " \"shortName\": \"qe\",\n", + " \"summary\": \"Quantum Espresso\",\n", + " \"build\": \"Intel\",\n", + " \"version\": \"6.3\",\n", + " \"isDefault\": False,\n", + " \"hasAdvancedComputeOptions\": True,\n", + " \"schemaVersion\": \"2022.8.16\",\n", + " \"createdAt\": \"2023-04-13T09:42:52.622Z\",\n", + " \"createdBy\": \"0\",\n", + " \"updatedAt\": \"2023-06-15T20:01:39.744Z\",\n", + " \"updatedBy\": \"0\",\n", " },\n", - " \"head\": \"true\",\n", + " \"flowchartId\": \"066bb68919ae51b993ce7739\",\n", + " \"status\": \"idle\",\n", + " \"statusTrack\": [],\n", + " \"results\": [\n", + " {\"name\": \"total_energy\"},\n", + " {\"name\": \"total_energy_contributions\"},\n", + " {\"name\": \"pressure\"},\n", + " {\"name\": \"fermi_energy\"},\n", + " {\"name\": \"atomic_forces\"},\n", + " {\"name\": \"total_force\"},\n", + " {\"name\": \"stress_tensor\"},\n", + " ],\n", + " \"monitors\": [{\"name\": \"standard_output\"}, {\"name\": \"convergence_electronic\"}],\n", + " \"preProcessors\": [],\n", + " \"postProcessors\": [],\n", + " \"head\": True,\n", " \"input\": [\n", " {\n", - " \"content\": \"#!/bin/bash\\n# ---------------------- CLUSTER PARAMETERS ---------------------- #\\n#PBS -N Silicon-SCF\\n#PBS -j oe\\n#PBS -l nodes=1\\n#PBS -l ppn=4\\n#PBS -q OR\\n#PBS -l walltime=00:00:30:00\\n#PBS -A seminar-espresso-tutorials\\n# ------------------------- INPUT FILES -------------------------- #\\n# switch to the job working directory\\ncd $PBS_O_WORKDIR\\n\\nBASE_URL='https://github.com/pranabdas/espresso/raw/next/src'\\nIN_FILE='pw.scf.silicon.in'\\nPP_FILE='Si.pz-vbc.UPF'\\n\\n# get QE input file\\nwget ${BASE_URL}/silicon/${IN_FILE} -O ${IN_FILE}\\n\\n# get pseudopotential file\\nwget ${BASE_URL}/pseudos/${PP_FILE} -O ${PP_FILE}\\n\\n# delete pseudo_dir from input file and provide via env\\nsed -i '/^\\\\s*pseudo_dir/d' ${IN_FILE}\\nexport ESPRESSO_PSEUDO='./'\\n\\n# --------------------------- RUN JOB ---------------------------- #\\n# load required module\\nmodule add espresso/63-i-174-impi-044\\n\\n# run the calculation\\nmpirun -np $PBS_NP pw.x -in ${IN_FILE} | tee ${IN_FILE%*.in}.out\\n\\n# ----------------------------- END ------------------------------ #\\n\",\n", - " \"name\": \"pbs-Silicon-scf.sh\",\n", + " \"content\": \"{% if subworkflowContext.MATERIAL_INDEX %}\\n{%- set input = input.perMaterial[subworkflowContext.MATERIAL_INDEX] -%}\\n{% endif -%}\\n&CONTROL\\n calculation = 'scf'\\n title = ''\\n verbosity = 'low'\\n restart_mode = '{{ input.RESTART_MODE }}'\\n wf_collect = .true.\\n tstress = .true.\\n tprnfor = .true.\\n outdir = {% raw %}'{{ JOB_WORK_DIR }}/outdir'{% endraw %}\\n wfcdir = {% raw %}'{{ JOB_WORK_DIR }}/outdir'{% endraw %}\\n prefix = '__prefix__'\\n pseudo_dir = {% raw %}'{{ JOB_WORK_DIR }}/pseudo'{% endraw %}\\n/\\n&SYSTEM\\n ibrav = {{ input.IBRAV }}\\n nat = {{ input.NAT }}\\n ntyp = {{ input.NTYP }}\\n ecutwfc = {{ cutoffs.wavefunction }}\\n ecutrho = {{ cutoffs.density }}\\n occupations = 'smearing'\\n degauss = 0.005\\n/\\n&ELECTRONS\\n diagonalization = 'david'\\n diago_david_ndim = 4\\n diago_full_acc = .true.\\n mixing_beta = 0.3\\n startingwfc = 'atomic+random'\\n/\\n&IONS\\n/\\n&CELL\\n/\\nATOMIC_SPECIES\\n{{ input.ATOMIC_SPECIES }}\\nATOMIC_POSITIONS crystal\\n{{ input.ATOMIC_POSITIONS }}\\nCELL_PARAMETERS angstrom\\n{{ input.CELL_PARAMETERS }}\\nK_POINTS automatic\\n{% for d in kgrid.dimensions %}{{d}} {% endfor %}{% for s in kgrid.shifts %}{{s}} {% endfor %}\\n\",\n", + " \"name\": \"pw_scf.in\",\n", + " \"contextProviders\": [\n", + " {\"name\": \"KGridFormDataManager\"},\n", + " {\"name\": \"QEPWXInputDataManager\"},\n", + " {\"name\": \"PlanewaveCutoffDataManager\"},\n", + " ],\n", + " \"applicationName\": \"espresso\",\n", + " \"executableName\": \"pw.x\",\n", + " \"rendered\": \"&CONTROL\\n calculation = 'scf'\\n title = ''\\n verbosity = 'low'\\n restart_mode = 'from_scratch'\\n wf_collect = .true.\\n tstress = .true.\\n tprnfor = .true.\\n outdir = '{{ JOB_WORK_DIR }}/outdir'\\n wfcdir = '{{ JOB_WORK_DIR }}/outdir'\\n prefix = '__prefix__'\\n pseudo_dir = '{{ JOB_WORK_DIR }}/pseudo'\\n/\\n&SYSTEM\\n ibrav = 0\\n nat = 2\\n ntyp = 1\\n ecutwfc = 40\\n ecutrho = 200\\n occupations = 'smearing'\\n degauss = 0.005\\n/\\n&ELECTRONS\\n diagonalization = 'david'\\n diago_david_ndim = 4\\n diago_full_acc = .true.\\n mixing_beta = 0.3\\n startingwfc = 'atomic+random'\\n/\\n&IONS\\n/\\n&CELL\\n/\\nATOMIC_SPECIES\\nSi 28.0855 si_pbe_gbrv_1.0.upf\\nATOMIC_POSITIONS crystal\\nSi 0.000000000 0.000000000 0.000000000 \\nSi 0.250000000 0.250000000 0.250000000 \\nCELL_PARAMETERS angstrom\\n3.348920236 0.000000000 1.933500000\\n1.116307420 3.157392040 1.933500000\\n0.000000000 0.000000000 3.867000000\\nK_POINTS automatic\\n5 5 5 0 0 0 \\n\",\n", " }\n", " ],\n", - " \"name\": \"Silicon-SCF.sh\",\n", + " \"context\": {\n", + " \"kgridExtraData\": {\"materialHash\": \"a665723ef7429caef6ca89385fe25bae\"},\n", + " \"kgrid\": {\n", + " \"dimensions\": [5, 5, 5],\n", + " \"shifts\": [0, 0, 0],\n", + " \"reciprocalVectorRatios\": [1, 1, 1],\n", + " \"gridMetricType\": \"KPPRA\",\n", + " \"gridMetricValue\": 250,\n", + " \"preferGridMetric\": False,\n", + " },\n", + " \"isKgridEdited\": True,\n", + " \"subworkflowContext\": {},\n", + " },\n", " \"executable\": {\n", - " \"isDefault\": \"true\",\n", - " \"monitors\": [\"standard_output\"],\n", + " \"isDefault\": True,\n", + " \"hasAdvancedComputeOptions\": True,\n", + " \"postProcessors\": [\"remove_non_zero_weight_kpoints\"],\n", + " \"monitors\": [\"standard_output\", \"convergence_ionic\", \"convergence_electronic\"],\n", " \"results\": [\n", " \"atomic_forces\",\n", " \"band_gaps\",\n", - " \"band_structure\",\n", " \"density_of_states\",\n", " \"fermi_energy\",\n", - " \"phonon_dispersions\",\n", - " \"phonon_dos\",\n", " \"pressure\",\n", " \"stress_tensor\",\n", " \"total_energy\",\n", " \"total_energy_contributions\",\n", " \"total_force\",\n", - " \"zero_point_energy\",\n", " \"final_structure\",\n", " \"magnetic_moments\",\n", " \"reaction_energy_barrier\",\n", @@ -158,32 +234,39 @@ " \"potential_profile\",\n", " \"charge_density_profile\",\n", " ],\n", - " \"name\": \"sh\",\n", + " \"name\": \"pw.x\",\n", " },\n", " \"flavor\": {\n", - " \"isDefault\": \"true\",\n", - " \"input\": [{\"name\": \"hello_world.sh\"}],\n", - " \"monitors\": [\"standard_output\"],\n", - " \"applicationName\": \"shell\",\n", - " \"executableName\": \"sh\",\n", - " \"name\": \"hello_world\",\n", + " \"isDefault\": True,\n", + " \"input\": [{\"name\": \"pw_scf.in\"}],\n", + " \"results\": [\n", + " \"total_energy\",\n", + " \"total_energy_contributions\",\n", + " \"pressure\",\n", + " \"fermi_energy\",\n", + " \"atomic_forces\",\n", + " \"total_force\",\n", + " \"stress_tensor\",\n", + " ],\n", + " \"monitors\": [\"standard_output\", \"convergence_electronic\"],\n", + " \"applicationName\": \"espresso\",\n", + " \"executableName\": \"pw.x\",\n", + " \"name\": \"pw_scf\",\n", " \"executable\": {\n", - " \"isDefault\": \"true\",\n", - " \"monitors\": [\"standard_output\"],\n", + " \"isDefault\": True,\n", + " \"hasAdvancedComputeOptions\": True,\n", + " \"postProcessors\": [\"remove_non_zero_weight_kpoints\"],\n", + " \"monitors\": [\"standard_output\", \"convergence_ionic\", \"convergence_electronic\"],\n", " \"results\": [\n", " \"atomic_forces\",\n", " \"band_gaps\",\n", - " \"band_structure\",\n", " \"density_of_states\",\n", " \"fermi_energy\",\n", - " \"phonon_dispersions\",\n", - " \"phonon_dos\",\n", " \"pressure\",\n", " \"stress_tensor\",\n", " \"total_energy\",\n", " \"total_energy_contributions\",\n", " \"total_force\",\n", - " \"zero_point_energy\",\n", " \"final_structure\",\n", " \"magnetic_moments\",\n", " \"reaction_energy_barrier\",\n", @@ -191,55 +274,185 @@ " \"potential_profile\",\n", " \"charge_density_profile\",\n", " ],\n", - " \"name\": \"sh\",\n", + " \"name\": \"pw.x\",\n", " },\n", " },\n", + " \"name\": \"pw_scf\",\n", " }\n", " ],\n", - " \"compute\": {\n", - " \"ppn\": 4,\n", - " \"nodes\": 1,\n", - " \"queue\": \"OR\",\n", - " \"timeLimit\": \"01:00:00\",\n", - " \"notify\": \"n\",\n", - " \"cluster\": {\"fqdn\": \"master-production-20160630-cluster-001.exabyte.io\"},\n", - " },\n", " }\n", " ],\n", + " \"units\": [{\"name\": \"Total energy\", \"type\": \"subworkflow\", \"head\": True, \"schemaVersion\": \"2022.8.16\"}],\n", + " \"properties\": [\n", + " \"total_energy\",\n", + " \"total_energy_contributions\",\n", + " \"pressure\",\n", + " \"fermi_energy\",\n", + " \"atomic_forces\",\n", + " \"total_force\",\n", + " \"stress_tensor\",\n", + " ],\n", "}" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "19037ef9-d5f7-4344-90ea-e7cbf8bbeba0", "metadata": {}, - "outputs": [ - { - "ename": "HTTPError", - "evalue": "500 Server Error: Internal Server Error for url: https://platform.mat3ra.com:443/api/2018-10-01/workflows/create", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mHTTPError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[4], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mendpoint\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcreate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mBODY\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/Documents/api-examples/.env/lib/python3.8/site-packages/exabyte_api_client/endpoints/entity.py:89\u001b[0m, in \u001b[0;36mEntityEndpoint.create\u001b[0;34m(self, config)\u001b[0m\n\u001b[1;32m 79\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcreate\u001b[39m(\u001b[38;5;28mself\u001b[39m, config):\n\u001b[1;32m 80\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 81\u001b[0m \u001b[38;5;124;03m Creates a new entity.\u001b[39;00m\n\u001b[1;32m 82\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 87\u001b[0m \u001b[38;5;124;03m dict: new entity.\u001b[39;00m\n\u001b[1;32m 88\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m---> 89\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mPUT\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m/\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m(\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mcreate\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjson\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdumps\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mheaders\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/Documents/api-examples/.env/lib/python3.8/site-packages/exabyte_api_client/endpoints/__init__.py:38\u001b[0m, in \u001b[0;36mBaseEndpoint.request\u001b[0;34m(self, method, endpoint_path, params, data, headers)\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 25\u001b[0m \u001b[38;5;124;03mSends an HTTP request with given params, headers and data to the given endpoint.\u001b[39;00m\n\u001b[1;32m 26\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 35\u001b[0m \u001b[38;5;124;03m json: response\u001b[39;00m\n\u001b[1;32m 36\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 37\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconn:\n\u001b[0;32m---> 38\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconn\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mendpoint_path\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 39\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconn\u001b[38;5;241m.\u001b[39mjson()\n\u001b[1;32m 40\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m response[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstatus\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m!=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msuccess\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n", - "File \u001b[0;32m~/Documents/api-examples/.env/lib/python3.8/site-packages/exabyte_api_client/utils/http.py:147\u001b[0m, in \u001b[0;36mConnection.request\u001b[0;34m(self, method, endpoint_path, params, data, headers)\u001b[0m\n\u001b[1;32m 136\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 137\u001b[0m \u001b[38;5;124;03mSends an HTTP request with given params, headers and data to the given endpoint.\u001b[39;00m\n\u001b[1;32m 138\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 144\u001b[0m \u001b[38;5;124;03m params (dict): URL parameters to append to the URL.\u001b[39;00m\n\u001b[1;32m 145\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 146\u001b[0m url \u001b[38;5;241m=\u001b[39m urllib\u001b[38;5;241m.\u001b[39mparse\u001b[38;5;241m.\u001b[39murljoin(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpreamble, endpoint_path)\n\u001b[0;32m--> 147\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mConnection\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43murl\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/Documents/api-examples/.env/lib/python3.8/site-packages/exabyte_api_client/utils/http.py:35\u001b[0m, in \u001b[0;36mBaseConnection.request\u001b[0;34m(self, method, url, params, data, headers)\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 25\u001b[0m \u001b[38;5;124;03mSends an HTTP request with given params, headers and data to the given url.\u001b[39;00m\n\u001b[1;32m 26\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[38;5;124;03m params (dict): URL parameters to append to the URL.\u001b[39;00m\n\u001b[1;32m 33\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 34\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mresponse \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msession\u001b[38;5;241m.\u001b[39mrequest(method\u001b[38;5;241m=\u001b[39mmethod\u001b[38;5;241m.\u001b[39mlower(), url\u001b[38;5;241m=\u001b[39murl, params\u001b[38;5;241m=\u001b[39mparams, data\u001b[38;5;241m=\u001b[39mdata, headers\u001b[38;5;241m=\u001b[39mheaders)\n\u001b[0;32m---> 35\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mresponse\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mraise_for_status\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/Documents/api-examples/.env/lib/python3.8/site-packages/requests/models.py:1021\u001b[0m, in \u001b[0;36mResponse.raise_for_status\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 1016\u001b[0m http_error_msg \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m 1017\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstatus_code\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m Server Error: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mreason\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m for url: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39murl\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1018\u001b[0m )\n\u001b[1;32m 1020\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m http_error_msg:\n\u001b[0;32m-> 1021\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m HTTPError(http_error_msg, response\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m)\n", - "\u001b[0;31mHTTPError\u001b[0m: 500 Server Error: Internal Server Error for url: https://platform.mat3ra.com:443/api/2018-10-01/workflows/create" - ] - } - ], + "outputs": [], + "source": [ + "# create workflow\n", + "WORKFLOW_RESP = workflow_endpoints.create(WORKFLOW_BODY)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "a34acdbb", + "metadata": {}, + "source": [ + "#### Get default material from the user account" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "837d5888-d036-4144-900e-bfada8436c91", + "metadata": {}, + "outputs": [], + "source": [ + "default_material = material_endpoints.list({\"isDefault\": True, \"owner._id\": ACCOUNT_ID})[0]\n", + "material_id = default_material[\"_id\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "41d85194-5a2b-4e65-9302-251932d04c11", + "metadata": {}, + "outputs": [], + "source": [ + "# job creation payload\n", + "JOB_BODY = {\n", + " \"name\": \"SCF Calculation\",\n", + " \"compute\": {\n", + " \"ppn\": 2,\n", + " \"nodes\": 1,\n", + " \"queue\": \"OR\",\n", + " \"cluster\": {\"fqdn\": \"master-production-20160630-cluster-001.exabyte.io\"},\n", + " },\n", + " \"_project\": {\"slug\": \"pranab-default\"},\n", + " \"workflow\": WORKFLOW_RESP,\n", + " \"_material\": {\"_id\": material_id},\n", + "}" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "556d0158", + "metadata": {}, "source": [ - "response = endpoint.create(BODY)" + "#### Create and submit job" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ed28b99-923e-4b30-a0d5-51fe45675c77", + "metadata": {}, + "outputs": [], + "source": [ + "# create job\n", + "JOB_RESP = job_endpoints.create(JOB_BODY)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa65092c-a30a-402e-85c2-506746e4664c", + "metadata": {}, + "outputs": [], + "source": [ + "# submit job\n", + "job_endpoints.submit(JOB_RESP[\"_id\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5129a629-a46a-4a4c-8de9-98228611aa87", + "metadata": {}, + "outputs": [], + "source": [ + "wait_for_jobs_to_finish(job_endpoints, [JOB_RESP[\"_id\"]])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "a0a57e12", + "metadata": {}, + "source": [ + "#### Get output file, perform post processing, and make plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87b8fa4b-39cb-4a11-b8da-b07303b9d42c", + "metadata": {}, + "outputs": [], + "source": [ + "files = job_endpoints.list_files(JOB_RESP[\"_id\"])\n", + "for file in files:\n", + " if file[\"name\"] == \"pw_scf.out\":\n", + " output_file_metadata = file\n", + "\n", + "import urllib\n", + "\n", + "server_response = urllib.request.urlopen(output_file_metadata[\"signedUrl\"])\n", + "output_file_bytes = server_response.read()\n", + "output_file = output_file_bytes.decode(encoding=\"UTF-8\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c160fc04-48c2-40da-b21a-db6706b93dd7", + "metadata": {}, + "outputs": [], + "source": [ + "energy = []\n", + "len_energy = len(\"total energy\")\n", + "for line in output_file.split(\"\\n\"):\n", + " if line.strip().lstrip(\"!\")[:len_energy] == \"total energy\":\n", + " energy.append(float(line.split(\"=\")[1].rstrip(\"Ry\")))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e89a2984-10d7-4e80-929a-4f818a603d6b", + "metadata": {}, + "outputs": [], + "source": [ + "# plot energy with iteration step\n", + "import matplotlib.pyplot as plt\n", + "\n", + "%matplotlib inline\n", + "\n", + "plt.plot(energy)\n", + "plt.xlabel(\"Number of iteration\")\n", + "plt.ylabel(\"Energy (Ry)\")\n", + "plt.show()" ] }, { "cell_type": "code", "execution_count": null, - "id": "36f5b2f4-6a1c-4ed8-a66a-6244311ac0f1", + "id": "f60ae282-e9f3-4e6b-9b82-09a79696974e", "metadata": {}, "outputs": [], "source": [] diff --git a/examples/workflow/qe_scf_calculation.py b/examples/workflow/qe_scf_calculation.py index 61567402..d5fc49cb 100644 --- a/examples/workflow/qe_scf_calculation.py +++ b/examples/workflow/qe_scf_calculation.py @@ -5,7 +5,7 @@ # Open in Google Colab # -# # Quantum Espresso SCF calcuation via API +# # Quantum Espresso SCF calculation via API # # # Complete Authorization Form and Initialize Settings @@ -46,76 +46,145 @@ get_ipython().system('GIT_BRANCH="dev"; export GIT_BRANCH; curl -s "https://raw.githubusercontent.com/Exabyte-io/api-examples/${GIT_BRANCH}/scripts/env.sh" | bash') -# In[1]: +# In[ ]: from utils.settings import ENDPOINT_ARGS, ACCOUNT_ID -from utils.generic import display_JSON +from utils.generic import display_JSON, wait_for_jobs_to_finish from exabyte_api_client.endpoints.workflows import WorkflowEndpoints +from exabyte_api_client.endpoints.materials import MaterialEndpoints +from exabyte_api_client.endpoints.jobs import JobEndpoints -# In[2]: +# In[ ]: # Initialize a helper class to interact with WorkflowEndpoints -endpoint = WorkflowEndpoints(*ENDPOINT_ARGS) +workflow_endpoints = WorkflowEndpoints(*ENDPOINT_ARGS) +material_endpoints = MaterialEndpoints(*ENDPOINT_ARGS) +job_endpoints = JobEndpoints(*ENDPOINT_ARGS) -# In[3]: +# #### Create a Quantum Espresso workflow for SCF calculation + +# In[ ]: # payload for workflow creation -BODY = { - "name": "Silicon-SCF-bash-REST", +WORKFLOW_BODY = { + "name": "Silicon SCF", "subworkflows": [ { - "name": "QE-SCF", + "name": "Total energy", "application": { - "name": "shell", - "version": "4.2.46", - "build": "Default", - "isDefault": "true", - "summary": "Shell Script", - "shortName": "sh", + "_id": "u2qtBhseFfQMWRYND", + "name": "espresso", + "shortName": "qe", + "summary": "Quantum Espresso", + "build": "Intel", + "version": "6.3", + "isDefault": False, + "hasAdvancedComputeOptions": True, + "schemaVersion": "2022.8.16", + "createdAt": "2023-04-13T09:42:52.622Z", + "createdBy": "0", + "updatedAt": "2023-06-15T20:01:39.744Z", + "updatedBy": "0", + }, + "properties": [ + "total_energy", + "total_energy_contributions", + "pressure", + "fermi_energy", + "atomic_forces", + "total_force", + "stress_tensor", + ], + "model": { + "type": "dft", + "subtype": "gga", + "method": {"type": "pseudopotential", "subtype": "us", "data": {"searchText": ""}}, + "functional": {"slug": "pbe"}, + "refiners": [], + "modifiers": [], }, - "properties": ["total_energy", "fermi_energy"], "units": [ { "type": "execution", "application": { - "name": "shell", - "version": "4.2.46", - "build": "Default", - "isDefault": "true", - "summary": "Shell Script", - "shortName": "sh", + "_id": "u2qtBhseFfQMWRYND", + "name": "espresso", + "shortName": "qe", + "summary": "Quantum Espresso", + "build": "Intel", + "version": "6.3", + "isDefault": False, + "hasAdvancedComputeOptions": True, + "schemaVersion": "2022.8.16", + "createdAt": "2023-04-13T09:42:52.622Z", + "createdBy": "0", + "updatedAt": "2023-06-15T20:01:39.744Z", + "updatedBy": "0", }, - "head": "true", + "flowchartId": "066bb68919ae51b993ce7739", + "status": "idle", + "statusTrack": [], + "results": [ + {"name": "total_energy"}, + {"name": "total_energy_contributions"}, + {"name": "pressure"}, + {"name": "fermi_energy"}, + {"name": "atomic_forces"}, + {"name": "total_force"}, + {"name": "stress_tensor"}, + ], + "monitors": [{"name": "standard_output"}, {"name": "convergence_electronic"}], + "preProcessors": [], + "postProcessors": [], + "head": True, "input": [ { - "content": "#!/bin/bash\n# ---------------------- CLUSTER PARAMETERS ---------------------- #\n#PBS -N Silicon-SCF\n#PBS -j oe\n#PBS -l nodes=1\n#PBS -l ppn=4\n#PBS -q OR\n#PBS -l walltime=00:00:30:00\n#PBS -A seminar-espresso-tutorials\n# ------------------------- INPUT FILES -------------------------- #\n# switch to the job working directory\ncd $PBS_O_WORKDIR\n\nBASE_URL='https://github.com/pranabdas/espresso/raw/next/src'\nIN_FILE='pw.scf.silicon.in'\nPP_FILE='Si.pz-vbc.UPF'\n\n# get QE input file\nwget ${BASE_URL}/silicon/${IN_FILE} -O ${IN_FILE}\n\n# get pseudopotential file\nwget ${BASE_URL}/pseudos/${PP_FILE} -O ${PP_FILE}\n\n# delete pseudo_dir from input file and provide via env\nsed -i '/^\\s*pseudo_dir/d' ${IN_FILE}\nexport ESPRESSO_PSEUDO='./'\n\n# --------------------------- RUN JOB ---------------------------- #\n# load required module\nmodule add espresso/63-i-174-impi-044\n\n# run the calculation\nmpirun -np $PBS_NP pw.x -in ${IN_FILE} | tee ${IN_FILE%*.in}.out\n\n# ----------------------------- END ------------------------------ #\n", - "name": "pbs-Silicon-scf.sh", + "content": "{% if subworkflowContext.MATERIAL_INDEX %}\n{%- set input = input.perMaterial[subworkflowContext.MATERIAL_INDEX] -%}\n{% endif -%}\n&CONTROL\n calculation = 'scf'\n title = ''\n verbosity = 'low'\n restart_mode = '{{ input.RESTART_MODE }}'\n wf_collect = .true.\n tstress = .true.\n tprnfor = .true.\n outdir = {% raw %}'{{ JOB_WORK_DIR }}/outdir'{% endraw %}\n wfcdir = {% raw %}'{{ JOB_WORK_DIR }}/outdir'{% endraw %}\n prefix = '__prefix__'\n pseudo_dir = {% raw %}'{{ JOB_WORK_DIR }}/pseudo'{% endraw %}\n/\n&SYSTEM\n ibrav = {{ input.IBRAV }}\n nat = {{ input.NAT }}\n ntyp = {{ input.NTYP }}\n ecutwfc = {{ cutoffs.wavefunction }}\n ecutrho = {{ cutoffs.density }}\n occupations = 'smearing'\n degauss = 0.005\n/\n&ELECTRONS\n diagonalization = 'david'\n diago_david_ndim = 4\n diago_full_acc = .true.\n mixing_beta = 0.3\n startingwfc = 'atomic+random'\n/\n&IONS\n/\n&CELL\n/\nATOMIC_SPECIES\n{{ input.ATOMIC_SPECIES }}\nATOMIC_POSITIONS crystal\n{{ input.ATOMIC_POSITIONS }}\nCELL_PARAMETERS angstrom\n{{ input.CELL_PARAMETERS }}\nK_POINTS automatic\n{% for d in kgrid.dimensions %}{{d}} {% endfor %}{% for s in kgrid.shifts %}{{s}} {% endfor %}\n", + "name": "pw_scf.in", + "contextProviders": [ + {"name": "KGridFormDataManager"}, + {"name": "QEPWXInputDataManager"}, + {"name": "PlanewaveCutoffDataManager"}, + ], + "applicationName": "espresso", + "executableName": "pw.x", + "rendered": "&CONTROL\n calculation = 'scf'\n title = ''\n verbosity = 'low'\n restart_mode = 'from_scratch'\n wf_collect = .true.\n tstress = .true.\n tprnfor = .true.\n outdir = '{{ JOB_WORK_DIR }}/outdir'\n wfcdir = '{{ JOB_WORK_DIR }}/outdir'\n prefix = '__prefix__'\n pseudo_dir = '{{ JOB_WORK_DIR }}/pseudo'\n/\n&SYSTEM\n ibrav = 0\n nat = 2\n ntyp = 1\n ecutwfc = 40\n ecutrho = 200\n occupations = 'smearing'\n degauss = 0.005\n/\n&ELECTRONS\n diagonalization = 'david'\n diago_david_ndim = 4\n diago_full_acc = .true.\n mixing_beta = 0.3\n startingwfc = 'atomic+random'\n/\n&IONS\n/\n&CELL\n/\nATOMIC_SPECIES\nSi 28.0855 si_pbe_gbrv_1.0.upf\nATOMIC_POSITIONS crystal\nSi 0.000000000 0.000000000 0.000000000 \nSi 0.250000000 0.250000000 0.250000000 \nCELL_PARAMETERS angstrom\n3.348920236 0.000000000 1.933500000\n1.116307420 3.157392040 1.933500000\n0.000000000 0.000000000 3.867000000\nK_POINTS automatic\n5 5 5 0 0 0 \n", } ], - "name": "Silicon-SCF.sh", + "context": { + "kgridExtraData": {"materialHash": "a665723ef7429caef6ca89385fe25bae"}, + "kgrid": { + "dimensions": [5, 5, 5], + "shifts": [0, 0, 0], + "reciprocalVectorRatios": [1, 1, 1], + "gridMetricType": "KPPRA", + "gridMetricValue": 250, + "preferGridMetric": False, + }, + "isKgridEdited": True, + "subworkflowContext": {}, + }, "executable": { - "isDefault": "true", - "monitors": ["standard_output"], + "isDefault": True, + "hasAdvancedComputeOptions": True, + "postProcessors": ["remove_non_zero_weight_kpoints"], + "monitors": ["standard_output", "convergence_ionic", "convergence_electronic"], "results": [ "atomic_forces", "band_gaps", - "band_structure", "density_of_states", "fermi_energy", - "phonon_dispersions", - "phonon_dos", "pressure", "stress_tensor", "total_energy", "total_energy_contributions", "total_force", - "zero_point_energy", "final_structure", "magnetic_moments", "reaction_energy_barrier", @@ -123,32 +192,39 @@ "potential_profile", "charge_density_profile", ], - "name": "sh", + "name": "pw.x", }, "flavor": { - "isDefault": "true", - "input": [{"name": "hello_world.sh"}], - "monitors": ["standard_output"], - "applicationName": "shell", - "executableName": "sh", - "name": "hello_world", + "isDefault": True, + "input": [{"name": "pw_scf.in"}], + "results": [ + "total_energy", + "total_energy_contributions", + "pressure", + "fermi_energy", + "atomic_forces", + "total_force", + "stress_tensor", + ], + "monitors": ["standard_output", "convergence_electronic"], + "applicationName": "espresso", + "executableName": "pw.x", + "name": "pw_scf", "executable": { - "isDefault": "true", - "monitors": ["standard_output"], + "isDefault": True, + "hasAdvancedComputeOptions": True, + "postProcessors": ["remove_non_zero_weight_kpoints"], + "monitors": ["standard_output", "convergence_ionic", "convergence_electronic"], "results": [ "atomic_forces", "band_gaps", - "band_structure", "density_of_states", "fermi_energy", - "phonon_dispersions", - "phonon_dos", "pressure", "stress_tensor", "total_energy", "total_energy_contributions", "total_force", - "zero_point_energy", "final_structure", "magnetic_moments", "reaction_energy_barrier", @@ -156,28 +232,122 @@ "potential_profile", "charge_density_profile", ], - "name": "sh", + "name": "pw.x", }, }, + "name": "pw_scf", } ], - "compute": { - "ppn": 4, - "nodes": 1, - "queue": "OR", - "timeLimit": "01:00:00", - "notify": "n", - "cluster": {"fqdn": "master-production-20160630-cluster-001.exabyte.io"}, - }, } ], + "units": [{"name": "Total energy", "type": "subworkflow", "head": True, "schemaVersion": "2022.8.16"}], + "properties": [ + "total_energy", + "total_energy_contributions", + "pressure", + "fermi_energy", + "atomic_forces", + "total_force", + "stress_tensor", + ], +} + + +# In[ ]: + + +# create workflow +WORKFLOW_RESP = workflow_endpoints.create(WORKFLOW_BODY) + + +# #### Get default material from the user account + +# In[ ]: + + +default_material = material_endpoints.list({"isDefault": True, "owner._id": ACCOUNT_ID})[0] +material_id = default_material["_id"] + + +# In[ ]: + + +# job creation payload +JOB_BODY = { + "name": "SCF Calculation", + "compute": { + "ppn": 2, + "nodes": 1, + "queue": "OR", + "cluster": {"fqdn": "master-production-20160630-cluster-001.exabyte.io"}, + }, + "_project": {"slug": "pranab-default"}, + "workflow": WORKFLOW_RESP, + "_material": {"_id": material_id}, } -# In[4]: +# #### Create and submit job + +# In[ ]: + + +# create job +JOB_RESP = job_endpoints.create(JOB_BODY) + + +# In[ ]: + + +# submit job +job_endpoints.submit(JOB_RESP["_id"]) + + +# In[ ]: + + +wait_for_jobs_to_finish(job_endpoints, [JOB_RESP["_id"]]) + + +# #### Get output file, perform post processing, and make plots + +# In[ ]: + + +files = job_endpoints.list_files(JOB_RESP["_id"]) +for file in files: + if file["name"] == "pw_scf.out": + output_file_metadata = file + +import urllib + +server_response = urllib.request.urlopen(output_file_metadata["signedUrl"]) +output_file_bytes = server_response.read() +output_file = output_file_bytes.decode(encoding="UTF-8") + + +# In[ ]: + + +energy = [] +len_energy = len("total energy") +for line in output_file.split("\n"): + if line.strip().lstrip("!")[:len_energy] == "total energy": + energy.append(float(line.split("=")[1].rstrip("Ry"))) + + +# In[ ]: + + +# plot energy with iteration step +import matplotlib.pyplot as plt +get_ipython().run_line_magic('matplotlib', 'inline') -response = endpoint.create(BODY) +plt.plot(energy) +plt.xlabel("Number of iteration") +plt.ylabel("Energy (Ry)") +plt.show() # In[ ]: