Skip to content

Commit

Permalink
Merge pull request #118 from Exabyte-io/feature/SOF-7339
Browse files Browse the repository at this point in the history
Feature/SOF-7339 update: rewrite ZSL notebook to use made-tools
  • Loading branch information
VsevolodX committed May 3, 2024
2 parents a87483c + d4a07d5 commit 90f54f6
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 193 deletions.
4 changes: 4 additions & 0 deletions config.yml
Expand Up @@ -31,6 +31,10 @@ notebooks:
- nbformat>=4.2.0
packages_python:
packages_pyodide:
- annotated_types>=0.6.0
- https://files.mat3ra.com/uploads/pydantic_core-2.18.2-py3-none-any.whl
- https://files.mat3ra.com/uploads/pydantic-2.7.1-py3-none-any.whl
- mat3ra-made
- name: import_material_from_jarvis_db_entry.ipynb
packages_common:
- express-py==2024.2.2.post2
Expand Down
225 changes: 40 additions & 185 deletions other/materials_designer/create_interface_with_min_strain_zsl.ipynb
Expand Up @@ -46,13 +46,11 @@
"outputs": [],
"source": [
"SUBSTRATE_PARAMETERS = {\n",
" \"MATERIAL_INDEX\": 0, # the index of the material in the materials_in list\n",
" \"MILLER_INDICES\": (1, 1, 1), # the miller indices of the interfacial plane\n",
" \"THICKNESS\": 3, # in layers\n",
"}\n",
"\n",
"LAYER_PARAMETERS = {\n",
" \"MATERIAL_INDEX\": 1, # the index of the material in the materials_in list\n",
" \"MILLER_INDICES\": (0, 0, 1), # the miller indices of the interfacial plane\n",
" \"THICKNESS\": 1, # in layers\n",
"}\n",
Expand Down Expand Up @@ -107,7 +105,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. Install Packages"
"## 2. Install Packages\n",
"The step executes only in Pyodide environment. For other environments, the packages should be installed via `pip install` as directed in README."
]
},
{
Expand All @@ -120,35 +119,30 @@
"if sys.platform == \"emscripten\":\n",
" import micropip\n",
" await micropip.install('mat3ra-api-examples', deps=False)\n",
"from utils.jupyterlite import install_packages\n",
"await install_packages(\"create_interface_with_min_strain_zsl.ipynb\",\"../../config.yml\")"
" from utils.jupyterlite import install_packages\n",
" await install_packages(\"create_interface_with_min_strain_zsl.ipynb\",\"../../config.yml\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. Load and prepare input Materials\n"
"## 3. Load input Materials\n"
]
},
{
"cell_type": "code",
"outputs": [],
"source": [
"from utils.jupyterlite import get_data\n",
"from pymatgen.analysis.structure_analyzer import SpacegroupAnalyzer\n",
"from src.utils import to_pymatgen\n",
"from mat3ra.made.material import Material\n",
"\n",
"# Get the list of input materials and load them into `materials_in` variable\n",
"get_data(\"materials_in\", globals())\n",
"\n",
"if \"materials_in\" in globals():\n",
" pymatgen_materials = [to_pymatgen(item) for item in materials_in]\n",
" if USE_CONVENTIONAL_CELL: pymatgen_materials = [SpacegroupAnalyzer(item).get_conventional_standard_structure() for\n",
" item in pymatgen_materials]\n",
"\n",
" for material in pymatgen_materials:\n",
" print(material, \"\\n\")"
"materials = [] \n",
"for material_config in globals()[\"materials_in\"]:\n",
" print(material_config, \"\\n\")\n",
" materials.append(Material(material_config))"
],
"metadata": {
"collapsed": false
Expand Down Expand Up @@ -177,66 +171,28 @@
},
"outputs": [],
"source": [
"from src.pymatgen_coherent_interface_builder import CoherentInterfaceBuilder, ZSLGenerator\n",
"from src.utils import translate_to_bottom\n",
"\n",
"# Translate the materials to the bottom of the cell to allow for multilayer heterostructures creation\n",
"pymatgen_materials = [translate_to_bottom(item) for item in pymatgen_materials]\n",
" \n",
"def create_interfaces(settings):\n",
" print(\"Creating interfaces...\")\n",
" zsl = ZSLGenerator(\n",
" max_area_ratio_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_AREA_TOL\"],\n",
" max_area=settings[\"ZSL_PARAMETERS\"][\"MAX_AREA\"],\n",
" max_length_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_LENGTH_TOL\"],\n",
" max_angle_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_ANGLE_TOL\"],\n",
" )\n",
"\n",
" cib = CoherentInterfaceBuilder(\n",
" substrate_structure=pymatgen_materials[settings[\"SUBSTRATE_PARAMETERS\"][\"MATERIAL_INDEX\"]],\n",
" film_structure=pymatgen_materials[settings[\"LAYER_PARAMETERS\"][\"MATERIAL_INDEX\"]],\n",
" substrate_miller=settings[\"SUBSTRATE_PARAMETERS\"][\"MILLER_INDICES\"],\n",
" film_miller=settings[\"LAYER_PARAMETERS\"][\"MILLER_INDICES\"],\n",
" zslgen=zsl,\n",
" strain_tol=settings[\"ZSL_PARAMETERS\"][\"STRAIN_TOL\"],\n",
" )\n",
"\n",
" # Find terminations\n",
" cib._find_terminations()\n",
" terminations = cib.terminations\n",
"from mat3ra.made.tools.build import create_interfaces\n",
"\n",
" # Create interfaces for each termination\n",
" interfaces = {}\n",
" for termination in terminations:\n",
" interfaces[termination] = []\n",
" for interface in cib.get_interfaces(\n",
" termination,\n",
" gap=settings[\"INTERFACE_PARAMETERS\"][\"DISTANCE_Z\"],\n",
" film_thickness=settings[\"LAYER_PARAMETERS\"][\"THICKNESS\"],\n",
" substrate_thickness=settings[\"SUBSTRATE_PARAMETERS\"][\"THICKNESS\"],\n",
" in_layers=True,\n",
" ):\n",
" # Wrap atoms to unit cell\n",
" interface[\"interface\"].make_supercell((1, 1, 1), to_unit_cell=True)\n",
" interfaces[termination].append(interface)\n",
" return interfaces, terminations\n",
"\n",
"\n",
"interfaces, terminations = create_interfaces(\n",
"interface_data_holder = create_interfaces(\n",
" substrate=materials[0],\n",
" layer=materials[1],\n",
" settings={\n",
" \"SUBSTRATE_PARAMETERS\": SUBSTRATE_PARAMETERS,\n",
" \"LAYER_PARAMETERS\": LAYER_PARAMETERS,\n",
" \"USE_CONVENTIONAL_CELL\": USE_CONVENTIONAL_CELL,\n",
" \"ZSL_PARAMETERS\": ZSL_PARAMETERS,\n",
" \"INTERFACE_PARAMETERS\": INTERFACE_PARAMETERS,\n",
" }\n",
" },\n",
" sort_by_strain_and_size=True,\n",
" remove_duplicates=True,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 5.2. Print out the interfaces and terminations"
"### 4.2. Print out the interfaces and terminations"
]
},
{
Expand All @@ -245,18 +201,15 @@
"metadata": {},
"outputs": [],
"source": [
"print(f'Found {len(terminations)} terminations')\n",
"for termination in terminations:\n",
" print(f\"Found {len(interfaces[termination])} interfaces for\", termination, \"termination\")"
"print(\"Found terminations:\", interface_data_holder.terminations)\n",
"print(f\"Number of interfaces for a termination: {len(interface_data_holder.get_interfaces_for_termination(0))}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. Sort interfaces by strain\n",
"\n",
"### 5.1. Sort all interfaces"
"### 4.2. Print out interfaces with the lowest strain for each termination"
]
},
{
Expand All @@ -265,48 +218,19 @@
"metadata": {},
"outputs": [],
"source": [
"# Could be \"strain\", \"von_mises_strain\", \"mean_abs_strain\"\n",
"strain_mode = \"mean_abs_strain\"\n",
"\n",
"\n",
"# Sort interfaces by the specified strain mode and number of sites\n",
"def sort_interfaces(interfaces, terminations):\n",
" sorted_interfaces = {}\n",
" for termination in terminations:\n",
" sorted_interfaces[termination] = sorted(\n",
" interfaces[termination], key=lambda x: (x[strain_mode], x[\"interface\"].num_sites)\n",
" )\n",
" return sorted_interfaces\n",
"\n",
"\n",
"sorted_interfaces = sort_interfaces(interfaces, terminations)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 5.2. Print out interfaces with lowest strain for each termination"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for termination in terminations:\n",
"for termination in interface_data_holder.terminations:\n",
" print(f\"Interface with lowest strain for termination {termination} (index 0):\")\n",
" first_interface = sorted_interfaces[termination][0]\n",
" print(\" strain:\", first_interface[strain_mode] * 100, \"%\")\n",
" print(\" number of atoms:\", first_interface[\"interface\"].num_sites)"
" interfaces = interface_data_holder.get_interfaces_for_termination(termination)\n",
" first_interface = interfaces[0]\n",
" print(f\" strain: {first_interface.get_mean_abs_strain() * 100:.3f}%\")\n",
" print(\" number of atoms:\", first_interface.num_sites)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. Plot the results\n",
"## 5. Plot the results\n",
"\n",
"Plot the number of atoms vs strain. Adjust the parameters as needed.\n"
]
Expand All @@ -317,91 +241,26 @@
"metadata": {},
"outputs": [],
"source": [
"import plotly.graph_objs as go\n",
"from collections import defaultdict\n",
"from utils.plot import plot_strain_vs_atoms\n",
"\n",
"PLOT_SETTINGS = {\n",
" \"HEIGHT\": 600,\n",
" \"X_SCALE\": \"log\", # or linear\n",
" \"Y_SCALE\": \"log\", # or linear\n",
"}\n",
"\n",
"plot_strain_vs_atoms(interface_data_holder, PLOT_SETTINGS)\n",
"\n",
"def plot_strain_vs_atoms(sorted_interfaces, terminations, settings):\n",
" # Create a mapping from termination to its index\n",
" termination_to_index = {termination: i for i, termination in enumerate(terminations)}\n",
"\n",
" grouped_interfaces = defaultdict(list)\n",
" for termination, interfaces in sorted_interfaces.items():\n",
" for index, interface_data in enumerate(interfaces):\n",
" strain_percentage = interface_data[\"mean_abs_strain\"] * 100\n",
" num_sites = interface_data[\"interface\"].num_sites\n",
" key = (strain_percentage, num_sites)\n",
" grouped_interfaces[key].append((index, termination))\n",
"\n",
" data = []\n",
" for (strain, num_sites), indices_and_terminations in grouped_interfaces.items():\n",
" termination_indices = defaultdict(list)\n",
" for index, termination in indices_and_terminations:\n",
" termination_indices[termination].append(index)\n",
" all_indices = [index for indices in termination_indices.values() for index in indices]\n",
" index_range = f\"{min(all_indices)}-{max(all_indices)}\" if len(all_indices) > 1 else str(min(all_indices))\n",
"\n",
" hover_text = \"<br>-----<br>\".join(\n",
" f\"Termination: {termination}<br>Termination index: {termination_to_index[termination]}<br>Interfaces Index Range: {index_range}<br>Strain: {strain:.2f}%<br>Atoms: {num_sites}\"\n",
" for termination, indices in termination_indices.items()\n",
" )\n",
" trace = go.Scatter(\n",
" x=[strain],\n",
" y=[num_sites],\n",
" text=[hover_text],\n",
" mode=\"markers\",\n",
" hoverinfo=\"text\",\n",
" name=f\"Indices: {index_range}\",\n",
" )\n",
" data.append(trace)\n",
"\n",
" layout = go.Layout(\n",
" xaxis=dict(title=\"Strain (%)\", type=settings[\"X_SCALE\"]),\n",
" yaxis=dict(title=\"Number of atoms\", type=settings[\"Y_SCALE\"]),\n",
" hovermode=\"closest\",\n",
" height=settings[\"HEIGHT\"],\n",
" legend_title_text=\"Interfaces Index Range\",\n",
" )\n",
" fig = go.Figure(data=data, layout=layout)\n",
" fig.show()\n",
"\n",
"\n",
"plot_strain_vs_atoms(sorted_interfaces, terminations, PLOT_SETTINGS)\n",
"\n",
"for i, termination in enumerate(terminations):\n",
" print(f\"Termination {i}:\", termination)"
"print(\"Terminations: \\n\", interface_data_holder.terminations)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 7. Select the interface to pass outside this kernel\n",
"## 6. Select the interface to pass outside this kernel\n",
"\n",
"### 7.1. Select the interface with the desired termination and strain\n",
"\n",
"The data in `sorted_interfaces` now contains an object with the following structure:\n",
"\n",
"```json\n",
"{\n",
" \"('C_P6/mmm_2', 'Si_R-3m_1')\": [\n",
" { ...interface for ('C_P6/mmm_2', 'Si_R-3m_1') at index 0...},\n",
" { ...interface for ('C_P6/mmm_2', 'Si_R-3m_1') at index 1...},\n",
" ...\n",
" ],\n",
" \"<termination at index 1>\": [\n",
" { ...interface for 'termination at index 1' at index 0...},\n",
" { ...interface for 'termination at index 1' at index 1...},\n",
" ...\n",
" ]\n",
"}\n",
"```\n",
"### 6.1. Select the interface with the desired termination and strain\n",
"\n",
"Select the index for termination first, and for it - the index in the list of corresponding interfaces sorted by strain (index 0 has minimum strain)."
]
Expand All @@ -412,19 +271,18 @@
"metadata": {},
"outputs": [],
"source": [
"termination_index = 0\n",
"number_of_interfaces_to_include = 1\n",
"\n",
"termination = terminations[termination_index]\n",
"\n",
"selected_interfaces = sorted_interfaces[termination][:number_of_interfaces_to_include]"
"# Could be either the termination as tuple, e.g. `('Ni_P6/mmm_1', 'C_C2/m_2')` or its index: `0`\n",
"termination_or_its_index = 0\n",
"# select the first interface with the lowest strain and the smallest number of atoms\n",
"interfaces_slice_range_or_index = 0\n",
"selected_interfaces = interface_data_holder.get_interfaces_as_materials(termination_or_its_index, interfaces_slice_range_or_index)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 7.2. Pass data to the outside runtime\n"
"### 6.2. Pass data to the outside runtime\n"
]
},
{
Expand All @@ -433,12 +291,9 @@
"metadata": {},
"outputs": [],
"source": [
"from src.utils import from_pymatgen\n",
"from utils.jupyterlite import set_data\n",
"\n",
"materials = list(map(lambda interface_config: from_pymatgen(interface_config[\"interface\"]), selected_interfaces))\n",
"\n",
"set_data(\"materials\", materials)"
"set_data(\"materials\", selected_interfaces)"
]
}
],
Expand Down
5 changes: 3 additions & 2 deletions other/materials_designer/src/utils.py
Expand Up @@ -137,8 +137,9 @@ def ase_to_pymatgen(atoms: Atoms):
Returns:
Structure: pymatgen Structure object
"""
poscar = ase_to_poscar(atoms)
structure = Structure.from_str(poscar, fmt="poscar")

adaptor = AseAtomsAdaptor()
structure = adaptor.get_structure(atoms)

return structure

Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Expand Up @@ -9,7 +9,8 @@ dependencies = [
"exabyte-api-client>=2023.6.13.post0",
"matplotlib>=3.4.1",
"pandas>=1.5.3",
"pymatgen>=2023.5.31",
"pymatgen>=2024.4.13",
"mat3ra-made>=2024.5.3.post0",
]

[project.optional-dependencies]
Expand Down

0 comments on commit 90f54f6

Please sign in to comment.