Skip to content

Commit b4bf01c

Browse files
authored
Merge pull request #166 from Exabyte-io/feature/SOF-7497
feature/SOF-7497 feat: 2D material grain boundary NB
2 parents 58b5c2d + f000eb5 commit b4bf01c

File tree

2 files changed

+323
-0
lines changed

2 files changed

+323
-0
lines changed

other/materials_designer/Introduction.ipynb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"\n",
6363
"### 3.3. Planar Defects\n",
6464
"#### [3.3.1. Grain Boundary in a 3D Crystal](create_grain_boundary_crystal.ipynb)\n",
65+
"#### [3.3.2. Grain Boundary in a 2D Material](create_grain_boundary_film.ipynb)\n",
6566
"\n",
6667
"\n",
6768
"## 4. Passivation.\n",
Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"source": [
6+
"# Create a 2D Surface Grain Boundary in a film\n",
7+
"\n",
8+
"Use commensurate lattice matching algorithm to create grain boundaries in 2D materials by finding appropriate twist angles between two orientations of the same material.\n",
9+
"\n",
10+
"<h2 style=\"color:green\">Usage</h2>\n",
11+
"\n",
12+
"1. Make sure to select Input Material (in the outer runtime) before running the notebook.\n",
13+
"1. Set notebook parameters in cell 1.1. below (or use the default values).\n",
14+
"1. Click \"Run\" > \"Run All\" to run all cells.\n",
15+
"1. Wait for the run to complete (depending on the parameters can take a few min).\n",
16+
"1. Scroll down to view results.\n",
17+
"\n",
18+
"## Notes\n",
19+
"\n",
20+
"1. We perform commensurate lattice matching to find valid supercells that achieve the desired twist angle.\n",
21+
"1. When the matching is finished, grain boundaries with angles close to the target are presented.\n",
22+
"1. The algorithm searches for supercell matrices within specified size limits.\n",
23+
"2. The two orientations are placed next to each other in the x-direction with a gap in between.\n",
24+
"1. Atoms on the edge of the left orientation are handled to overlap with the right orientation in the interfacial region.\n",
25+
"1. For more information, see the [Introduction](Introduction.ipynb) notebook.\n"
26+
],
27+
"metadata": {
28+
"collapsed": false
29+
},
30+
"id": "415ed707e27a6c8e"
31+
},
32+
{
33+
"cell_type": "markdown",
34+
"source": [
35+
"## 1. Prepare the Environment\n",
36+
"### 1.1. Set up the notebook\n",
37+
"Set the following flags to control the notebook behavior\n",
38+
"For more information on the parameters and algorithm, refer to [Grain Boundary Builder Source](https://github.com/Exabyte-io/made/blob/35b9f318f5d667e0f5af023f3178bc4404317ab0/src/py/mat3ra/made/tools/build/grain_boundary/builders.py#L103)\n",
39+
"`EDGE_INCLUSION_TOLERANCE` is a fine-tuning parameter that controls the inclusion of the edge atoms for both orientations in the gap.\n",
40+
"For example of Graphene at 17.9 degrees: orange and green atoms are present with the value of 0.5 Angstroms, with value of 0, they will not be included.\n",
41+
"<img src=\"https://i.imgur.com/QRgotXS.png\" alt=\"Edge Inclusion Tolerance\" width=\"400\"/>\n"
42+
],
43+
"metadata": {
44+
"collapsed": false
45+
},
46+
"id": "a080006df3785cc5"
47+
},
48+
{
49+
"cell_type": "code",
50+
"outputs": [],
51+
"source": [
52+
"# Material selection\n",
53+
"MATERIAL_INDEX = 0 # Index in the list of materials\n",
54+
"\n",
55+
"# Grain boundary parameters\n",
56+
"TARGET_TWIST_ANGLE = 17.9 # in degrees\n",
57+
"BOUNDARY_GAP = 2.0 # Gap between two orientations in X direction, in Angstroms\n",
58+
"XY_SUPERCELL_MATRIX = [[1, 0], [0, 1]] # Supercell matrix to be applied to each of the orientations before matching\n",
59+
"\n",
60+
"# Search algorithm parameters\n",
61+
"MAX_REPETITION = 6 # Maximum supercell matrix element value\n",
62+
"ANGLE_TOLERANCE = 2.5 # in degrees\n",
63+
"RETURN_FIRST_MATCH = True # If True, returns first solution within tolerance\n",
64+
"\n",
65+
"# Distance tolerance for two atoms to be considered too close. \n",
66+
"# Used when merging two orientations to remove the atoms of the first one. \n",
67+
"# Should be less than the expected bond length\n",
68+
"DISTANCE_TOLERANCE = 1.2 # in Angstroms\n",
69+
"\n",
70+
"# How much to expand inclusion of the edge atoms for both orientations and fill in the gap region.\n",
71+
"# A fine-tuning parameter\n",
72+
"EDGE_INCLUSION_TOLERANCE = 0.5 # in Angstroms\n",
73+
"\n",
74+
"# Visualization parameters\n",
75+
"SHOW_INTERMEDIATE_STEPS = True\n",
76+
"CELL_REPETITIONS_FOR_VISUALIZATION = [3, 3, 1]"
77+
],
78+
"metadata": {
79+
"collapsed": false
80+
},
81+
"id": "338ee3c51155e086",
82+
"execution_count": null
83+
},
84+
{
85+
"cell_type": "markdown",
86+
"source": [
87+
"### 1.2. Install packages\n",
88+
"The step executes only in Pyodide environment. For other environments, the packages should be installed via `pip install`."
89+
],
90+
"metadata": {
91+
"collapsed": false
92+
},
93+
"id": "6463f9bbcd3be7c7"
94+
},
95+
{
96+
"cell_type": "code",
97+
"outputs": [],
98+
"source": [
99+
"import sys\n",
100+
"\n",
101+
"if sys.platform == \"emscripten\":\n",
102+
" import micropip\n",
103+
"\n",
104+
" await micropip.install('mat3ra-api-examples', deps=False)\n",
105+
" from utils.jupyterlite import install_packages\n",
106+
"\n",
107+
" await install_packages(\"\", \"../../config.yml\")"
108+
],
109+
"metadata": {
110+
"collapsed": false
111+
},
112+
"id": "7e22d1f4da825575",
113+
"execution_count": null
114+
},
115+
{
116+
"cell_type": "markdown",
117+
"source": [
118+
"### 1.3. Load and preview input material"
119+
],
120+
"metadata": {
121+
"collapsed": false
122+
},
123+
"id": "4a1cfe15caa44c3e"
124+
},
125+
{
126+
"cell_type": "code",
127+
"outputs": [],
128+
"source": [
129+
"from utils.jupyterlite import get_materials\n",
130+
"\n",
131+
"materials = get_materials(globals())"
132+
],
133+
"metadata": {
134+
"collapsed": false
135+
},
136+
"id": "a1635c31132962f6",
137+
"execution_count": null
138+
},
139+
{
140+
"cell_type": "markdown",
141+
"source": [
142+
"## 2. Prepare Material\n",
143+
"### 2.1. Select and visualize initial material"
144+
],
145+
"metadata": {
146+
"collapsed": false
147+
},
148+
"id": "32b3ad775543b06f"
149+
},
150+
{
151+
"cell_type": "code",
152+
"outputs": [],
153+
"source": [
154+
"from utils.visualize import visualize_materials\n",
155+
"\n",
156+
"material = materials[MATERIAL_INDEX]\n",
157+
"\n",
158+
"if SHOW_INTERMEDIATE_STEPS:\n",
159+
" visualize_materials(material, repetitions=CELL_REPETITIONS_FOR_VISUALIZATION)"
160+
],
161+
"metadata": {
162+
"collapsed": false
163+
},
164+
"id": "61f0870d8104cd21",
165+
"execution_count": null
166+
},
167+
{
168+
"cell_type": "markdown",
169+
"source": [
170+
"## 3. Generate Surface Grain Boundary\n",
171+
"### 3.1. Set up grain boundary configuration and builder\n"
172+
],
173+
"metadata": {
174+
"collapsed": false
175+
},
176+
"id": "34d6c7a337f1e40b"
177+
},
178+
{
179+
"cell_type": "code",
180+
"outputs": [],
181+
"source": [
182+
"from mat3ra.made.tools.build.grain_boundary import (\n",
183+
" SurfaceGrainBoundaryConfiguration,\n",
184+
" SurfaceGrainBoundaryBuilderParameters,\n",
185+
" SurfaceGrainBoundaryBuilder\n",
186+
")\n",
187+
"\n",
188+
"config = SurfaceGrainBoundaryConfiguration(\n",
189+
" film=material,\n",
190+
" twist_angle=TARGET_TWIST_ANGLE,\n",
191+
" distance_z=BOUNDARY_GAP,\n",
192+
" gap=BOUNDARY_GAP,\n",
193+
" xy_supercell_matrix=XY_SUPERCELL_MATRIX\n",
194+
")\n",
195+
"\n",
196+
"params = SurfaceGrainBoundaryBuilderParameters(\n",
197+
" max_supercell_matrix_int=MAX_REPETITION,\n",
198+
" angle_tolerance=ANGLE_TOLERANCE,\n",
199+
" return_first_match=RETURN_FIRST_MATCH,\n",
200+
" edge_inclusion_tolerance=EDGE_INCLUSION_TOLERANCE,\n",
201+
" distance_tolerance=DISTANCE_TOLERANCE\n",
202+
")\n",
203+
"\n",
204+
"builder = SurfaceGrainBoundaryBuilder(build_parameters=params)"
205+
],
206+
"metadata": {
207+
"collapsed": false
208+
},
209+
"id": "33a2c8a9be436745",
210+
"execution_count": null
211+
},
212+
{
213+
"cell_type": "markdown",
214+
"source": [
215+
"### 3.2. Generate and analyze grain boundaries\n"
216+
],
217+
"metadata": {
218+
"collapsed": false
219+
},
220+
"id": "79e9378bf5e144d4"
221+
},
222+
{
223+
"cell_type": "code",
224+
"outputs": [],
225+
"source": [
226+
"from utils.plot import plot_twisted_interface_solutions\n",
227+
"\n",
228+
"grain_boundaries = builder.get_materials(config)\n",
229+
"\n",
230+
"print(f\"\\nFound {len(grain_boundaries)} possible structures\")\n",
231+
"for i, gb in enumerate(grain_boundaries):\n",
232+
" actual_angle = gb.metadata.get(\"actual_twist_angle\", \"unknown\")\n",
233+
" print(f\"\\nGrain Boundary {i + 1}:\")\n",
234+
" print(f\"Actual twist angle: {actual_angle}°\")\n",
235+
" print(f\"Number of atoms: {len(gb.basis.elements.ids)}\")\n",
236+
"\n",
237+
"if len(grain_boundaries) > 0:\n",
238+
" plot_twisted_interface_solutions(grain_boundaries)"
239+
],
240+
"metadata": {
241+
"collapsed": false
242+
},
243+
"id": "d7007fe825463e5a",
244+
"execution_count": null
245+
},
246+
{
247+
"cell_type": "markdown",
248+
"source": [
249+
"## 4. Preview the selected grain boundary\n",
250+
"By default, the first grain boundary is selected. You can change the selection by changing the `selected_structure` index."
251+
],
252+
"metadata": {
253+
"collapsed": false
254+
},
255+
"id": "8b2f0574a20089a5"
256+
},
257+
{
258+
"cell_type": "code",
259+
"outputs": [],
260+
"source": [
261+
"selected_structure = grain_boundaries[0]\n",
262+
"actual_angle = selected_structure.metadata.get(\"build\").get(\"configuration\").get(\"actual_twist_angle\")\n",
263+
"print(f\"Target angle: {TARGET_TWIST_ANGLE}°\")\n",
264+
"print(f\"Actual angle: {actual_angle}°\")\n",
265+
"print(f\"Number of atoms: {len(selected_structure.basis.elements.ids)}\")\n",
266+
"\n",
267+
"visualize_materials(selected_structure, repetitions=[1, 1, 1])\n",
268+
"visualize_materials(selected_structure, repetitions=[1, 1, 1], rotation=\"-90x\")"
269+
],
270+
"metadata": {
271+
"collapsed": false
272+
},
273+
"id": "7f558a8e9d417cef",
274+
"execution_count": null
275+
},
276+
{
277+
"cell_type": "markdown",
278+
"source": [
279+
"### 5. Pass data to the outside runtime\n"
280+
],
281+
"metadata": {
282+
"collapsed": false
283+
},
284+
"id": "afcc004c5878b56f"
285+
},
286+
{
287+
"cell_type": "code",
288+
"outputs": [],
289+
"source": [
290+
"from utils.jupyterlite import set_materials\n",
291+
"\n",
292+
"set_materials(selected_structure)"
293+
],
294+
"metadata": {
295+
"collapsed": false
296+
},
297+
"id": "20e46167358d63",
298+
"execution_count": null
299+
}
300+
],
301+
"metadata": {
302+
"kernelspec": {
303+
"display_name": "Python 3",
304+
"language": "python",
305+
"name": "python3"
306+
},
307+
"language_info": {
308+
"codemirror_mode": {
309+
"name": "ipython",
310+
"version": 2
311+
},
312+
"file_extension": ".py",
313+
"mimetype": "text/x-python",
314+
"name": "python",
315+
"nbconvert_exporter": "python",
316+
"pygments_lexer": "ipython2",
317+
"version": "2.7.6"
318+
}
319+
},
320+
"nbformat": 4,
321+
"nbformat_minor": 5
322+
}

0 commit comments

Comments
 (0)