@mat3ra/made 2024.9.28-0 → 2024.10.2-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mat3ra/made",
3
- "version": "2024.9.28-0",
3
+ "version": "2024.10.2-0",
4
4
  "description": "MAterials DEsign library",
5
5
  "scripts": {
6
6
  "lint": "eslint --cache src/js tests/js && prettier --write src/js tests/js",
@@ -1,14 +1,17 @@
1
- from typing import Optional
1
+ from typing import Optional, Union
2
2
 
3
3
  from mat3ra.made.material import Material
4
4
 
5
- from .builders import SlabGrainBoundaryBuilder
6
- from .configuration import SlabGrainBoundaryConfiguration
5
+ from .builders import SlabGrainBoundaryBuilder, SurfaceGrainBoundaryBuilder, SurfaceGrainBoundaryBuilderParameters
6
+ from .configuration import (
7
+ SlabGrainBoundaryConfiguration,
8
+ SurfaceGrainBoundaryConfiguration,
9
+ )
7
10
 
8
11
 
9
12
  def create_grain_boundary(
10
- configuration: SlabGrainBoundaryConfiguration,
11
- builder: Optional[SlabGrainBoundaryBuilder] = None,
13
+ configuration: Union[SlabGrainBoundaryConfiguration, SurfaceGrainBoundaryConfiguration],
14
+ builder: Union[SlabGrainBoundaryBuilder, SurfaceGrainBoundaryBuilder, None] = None,
12
15
  ) -> Material:
13
16
  """
14
17
  Create a grain boundary according to provided configuration with selected builder.
@@ -3,13 +3,19 @@ from typing import List
3
3
  import numpy as np
4
4
  from mat3ra.made.material import Material
5
5
 
6
- from ..slab import SlabConfiguration, get_terminations, create_slab
6
+ from ...third_party import PymatgenInterface
7
7
  from ...analyze import get_chemical_formula
8
+ from ..slab import SlabConfiguration, get_terminations, create_slab
8
9
  from ..interface import ZSLStrainMatchingInterfaceBuilderParameters, InterfaceConfiguration
9
- from ..interface.builders import ZSLStrainMatchingInterfaceBuilder
10
+
11
+ from ..interface.builders import (
12
+ ZSLStrainMatchingInterfaceBuilder,
13
+ CommensurateLatticeTwistedInterfaceBuilder,
14
+ CommensurateLatticeTwistedInterfaceBuilderParameters,
15
+ )
10
16
  from ..supercell import create_supercell
11
- from .configuration import SlabGrainBoundaryConfiguration
12
- from ...third_party import PymatgenInterface
17
+ from ..utils import stack_two_materials_xy
18
+ from .configuration import SurfaceGrainBoundaryConfiguration, SlabGrainBoundaryConfiguration
13
19
 
14
20
 
15
21
  class SlabGrainBoundaryBuilderParameters(ZSLStrainMatchingInterfaceBuilderParameters):
@@ -27,6 +33,7 @@ class SlabGrainBoundaryBuilder(ZSLStrainMatchingInterfaceBuilder):
27
33
  """
28
34
 
29
35
  _BuildParametersType: type(SlabGrainBoundaryBuilderParameters) = SlabGrainBoundaryBuilderParameters # type: ignore
36
+ _DefaultBuildParameters = SlabGrainBoundaryBuilderParameters()
30
37
  _ConfigurationType: type(SlabGrainBoundaryConfiguration) = SlabGrainBoundaryConfiguration # type: ignore
31
38
  _GeneratedItemType: PymatgenInterface = PymatgenInterface # type: ignore
32
39
  selector_parameters: type( # type: ignore
@@ -78,3 +85,46 @@ class SlabGrainBoundaryBuilder(ZSLStrainMatchingInterfaceBuilder):
78
85
  )
79
86
  material.name = new_name
80
87
  return material
88
+
89
+
90
+ class SurfaceGrainBoundaryBuilderParameters(CommensurateLatticeTwistedInterfaceBuilderParameters):
91
+ """
92
+ Parameters for creating a grain boundary between two surface phases.
93
+
94
+ Args:
95
+ edge_inclusion_tolerance (float): The tolerance to include atoms on the edge of each phase, in angstroms.
96
+ distance_tolerance (float): The distance tolerance to remove atoms that are too close, in angstroms.
97
+ """
98
+
99
+ edge_inclusion_tolerance: float = 1.0
100
+ distance_tolerance: float = 1.0
101
+
102
+
103
+ class SurfaceGrainBoundaryBuilder(CommensurateLatticeTwistedInterfaceBuilder):
104
+ _ConfigurationType: type(SurfaceGrainBoundaryConfiguration) = SurfaceGrainBoundaryConfiguration # type: ignore
105
+ _BuildParametersType = SurfaceGrainBoundaryBuilderParameters
106
+ _DefaultBuildParameters = SurfaceGrainBoundaryBuilderParameters()
107
+
108
+ def _post_process(self, items: List[Material], post_process_parameters=None) -> List[Material]:
109
+ grain_boundaries = []
110
+ for item in items:
111
+ matrix1 = np.dot(np.array(item.configuration.xy_supercell_matrix), item.matrix1)
112
+ matrix2 = np.dot(np.array(item.configuration.xy_supercell_matrix), item.matrix2)
113
+ phase_1_material_initial = create_supercell(item.configuration.film, matrix1.tolist())
114
+ phase_2_material_initial = create_supercell(item.configuration.film, matrix2.tolist())
115
+
116
+ interface = stack_two_materials_xy(
117
+ phase_1_material_initial,
118
+ phase_2_material_initial,
119
+ gap=item.configuration.gap,
120
+ edge_inclusion_tolerance=self.build_parameters.edge_inclusion_tolerance,
121
+ distance_tolerance=self.build_parameters.distance_tolerance,
122
+ )
123
+ grain_boundaries.append(interface)
124
+
125
+ return grain_boundaries
126
+
127
+ def _update_material_name(self, material: Material, configuration: SurfaceGrainBoundaryConfiguration) -> Material:
128
+ new_name = f"{configuration.film.name}, Grain Boundary ({configuration.twist_angle:.2f}°)"
129
+ material.name = new_name
130
+ return material
@@ -1,8 +1,9 @@
1
- from typing import Optional
1
+ from typing import Optional, List
2
2
 
3
3
  from .. import BaseConfiguration
4
4
  from ..slab.configuration import SlabConfiguration
5
5
  from ..slab.termination import Termination
6
+ from ..interface.configuration import TwistedInterfaceConfiguration
6
7
 
7
8
 
8
9
  class SlabGrainBoundaryConfiguration(BaseConfiguration):
@@ -38,3 +39,24 @@ class SlabGrainBoundaryConfiguration(BaseConfiguration):
38
39
  "gap": self.gap,
39
40
  "slab_configuration": self.slab_configuration.to_json(),
40
41
  }
42
+
43
+
44
+ class SurfaceGrainBoundaryConfiguration(TwistedInterfaceConfiguration):
45
+ """
46
+ Configuration for creating a surface grain boundary.
47
+
48
+ Args:
49
+ gap (float): The gap between the two phases.
50
+ xy_supercell_matrix (List[List[int]]): The supercell matrix to apply for both phases.
51
+ """
52
+
53
+ gap: float = 0.0
54
+ xy_supercell_matrix: List[List[int]] = [[1, 0], [0, 1]]
55
+
56
+ @property
57
+ def _json(self):
58
+ return {
59
+ "type": self.get_cls_name(),
60
+ "gap": self.gap,
61
+ "xy_supercell_matrix": self.xy_supercell_matrix,
62
+ }
@@ -247,7 +247,7 @@ class NanoRibbonTwistedInterfaceBuilder(BaseBuilder):
247
247
  return material
248
248
 
249
249
 
250
- class CommensurateLatticeInterfaceBuilderParameters(BaseModel):
250
+ class CommensurateLatticeTwistedInterfaceBuilderParameters(BaseModel):
251
251
  """
252
252
  Parameters for the commensurate lattice interface builder.
253
253
 
@@ -263,7 +263,7 @@ class CommensurateLatticeInterfaceBuilderParameters(BaseModel):
263
263
  return_first_match: bool = False
264
264
 
265
265
 
266
- class CommensurateLatticeInterfaceBuilder(BaseBuilder):
266
+ class CommensurateLatticeTwistedInterfaceBuilder(BaseBuilder):
267
267
  _GeneratedItemType: type(CommensurateLatticePair) = CommensurateLatticePair # type: ignore
268
268
  _ConfigurationType: type(TwistedInterfaceConfiguration) = TwistedInterfaceConfiguration # type: ignore
269
269
 
@@ -1,3 +1,5 @@
1
+ from typing import Optional
2
+
1
3
  from mat3ra.code.entity import InMemoryEntity
2
4
  from pydantic import BaseModel
3
5
 
@@ -35,7 +37,7 @@ class InterfaceConfiguration(BaseModel, InMemoryEntity):
35
37
 
36
38
  class TwistedInterfaceConfiguration(BaseConfiguration):
37
39
  film: Material
38
- substrate: Material
40
+ substrate: Optional[Material] = None
39
41
  twist_angle: float = 0.0
40
42
  distance_z: float = 3.0
41
43
 
@@ -44,7 +46,7 @@ class TwistedInterfaceConfiguration(BaseConfiguration):
44
46
  return {
45
47
  "type": self.get_cls_name(),
46
48
  "film": self.film.to_json(),
47
- "substrate": self.substrate.to_json(),
49
+ "substrate": self.substrate.to_json() if self.substrate else None,
48
50
  "twist_angle": self.twist_angle,
49
51
  "distance_z": self.distance_z,
50
52
  }
@@ -3,6 +3,7 @@ from pydantic import BaseModel
3
3
 
4
4
  from mat3ra.made.material import Material
5
5
 
6
+ from ...modify import add_vacuum
6
7
  from ...third_party import PymatgenSlab, PymatgenSlabGenerator, label_pymatgen_slab_termination
7
8
  from ...analyze import get_chemical_formula
8
9
  from ...convert import to_pymatgen
@@ -28,7 +29,7 @@ class SlabBuilder(ConvertGeneratedItemsPymatgenStructureMixin, BaseBuilder):
28
29
  initial_structure=to_pymatgen(configuration.bulk),
29
30
  miller_index=configuration.miller_indices,
30
31
  min_slab_size=configuration.thickness,
31
- min_vacuum_size=configuration.vacuum,
32
+ min_vacuum_size=0,
32
33
  in_unit_planes=True,
33
34
  reorient_lattice=True,
34
35
  primitive=configuration.make_primitive,
@@ -47,12 +48,13 @@ class SlabBuilder(ConvertGeneratedItemsPymatgenStructureMixin, BaseBuilder):
47
48
  def _post_process(self, items: List[_GeneratedItemType], post_process_parameters=None) -> List[Material]:
48
49
  materials = super()._post_process(items, post_process_parameters)
49
50
  materials = [create_supercell(material, self.__configuration.xy_supercell_matrix) for material in materials]
50
- for idx, material in enumerate(materials):
51
+ materials_with_vacuum = [add_vacuum(material, self.__configuration.vacuum) for material in materials]
52
+ for idx, material in enumerate(materials_with_vacuum):
51
53
  if "build" not in material.metadata:
52
54
  material.metadata["build"] = {}
53
55
  material.metadata["build"]["termination"] = label_pymatgen_slab_termination(items[idx])
54
56
 
55
- return materials
57
+ return materials_with_vacuum
56
58
 
57
59
  def get_terminations(self, configuration: _ConfigurationType) -> List[Termination]:
58
60
  return [
@@ -1,4 +1,4 @@
1
- from typing import List, Tuple, Any
1
+ from typing import List, Tuple
2
2
 
3
3
  import numpy as np
4
4
 
@@ -17,18 +17,17 @@ class SlabConfiguration(BaseConfiguration):
17
17
  bulk (Material): The bulk material.
18
18
  miller_indices (Tuple[int, int, int]): The Miller indices of the slab.
19
19
  thickness (int): The thickness of the slab.
20
- vacuum (float): The vacuum thickness.
20
+ vacuum (float): The vacuum thickness, in Angstroms.
21
21
  xy_supercell_matrix (List[List[int]]): The supercell matrix for the xy plane.
22
22
  use_conventional_cell (bool): Whether to use the conventional cell.
23
23
  use_orthogonal_z (bool): Whether to use orthogonal z.
24
24
  make_primitive (bool): Whether to try to find primitive cell for the created slab.
25
25
  """
26
26
 
27
- # TODO: fix arbitrary_types_allowed error and set Material class type
28
- bulk: Any
27
+ bulk: Material
29
28
  miller_indices: Tuple[int, int, int] = (0, 0, 1)
30
29
  thickness: int = 1
31
- vacuum: int = 1
30
+ vacuum: float = 5.0
32
31
  xy_supercell_matrix: List[List[int]] = np.eye(2).tolist()
33
32
  use_conventional_cell: bool = True
34
33
  use_orthogonal_z: bool = False
@@ -3,6 +3,9 @@ from scipy.spatial import cKDTree
3
3
  from typing import List, Optional
4
4
  from mat3ra.made.basis import Basis
5
5
  from mat3ra.made.material import Material
6
+
7
+ from .supercell import create_supercell
8
+ from ..modify import filter_by_box, translate_by_vector
6
9
  from ...utils import ArrayWithIds
7
10
 
8
11
 
@@ -78,6 +81,20 @@ def merge_materials(
78
81
  distance_tolerance: float = 0.01,
79
82
  merge_dangerously=False,
80
83
  ) -> Material:
84
+ """
85
+ Merge multiple materials into a single material.
86
+
87
+ If some of the atoms are considered too close within a tolerance, only the last atom is kept.
88
+
89
+ Args:
90
+ materials (List[Material]): List of materials to merge.
91
+ material_name (Optional[str]): Name of the merged material.
92
+ distance_tolerance (float): The tolerance to replace atoms that are considered too close with respect
93
+ to the coordinates in the last material in the list, in angstroms.
94
+ merge_dangerously (bool): If True, the lattices are merged "as is" with no sanity checks.
95
+ Returns:
96
+ Material: The merged material.
97
+ """
81
98
  merged_material = materials[0]
82
99
  for material in materials[1:]:
83
100
  merged_material = merge_two_materials(
@@ -85,3 +102,77 @@ def merge_materials(
85
102
  )
86
103
 
87
104
  return merged_material
105
+
106
+
107
+ def double_and_filter_material(material: Material, start: List[float], end: List[float]) -> Material:
108
+ """
109
+ Double the material and filter it by a box defined by the start and end coordinates.
110
+ Args:
111
+ material (Material): The material to double and filter.
112
+ start (List[float]): The start coordinates of the box.
113
+ end (List[float]): The end coordinates of the box.
114
+ Returns:
115
+ Material: The filtered material.
116
+ """
117
+ material_doubled = create_supercell(material, scaling_factor=[2, 1, 1])
118
+ return filter_by_box(material_doubled, start, end)
119
+
120
+
121
+ def expand_lattice_vectors(material: Material, gap: float, direction: int = 0) -> Material:
122
+ """
123
+ Expand the lattice vectors of the material in the specified direction by the given gap.
124
+
125
+ Args:
126
+ material (Material): The material whose lattice vectors are to be expanded.
127
+ gap (float): The gap by which to expand the lattice vector.
128
+ direction (int): The index of the lattice vector to expand (0, 1, or 2).
129
+ """
130
+ new_lattice_vectors = material.lattice.vector_arrays
131
+ new_lattice_vectors[direction][direction] += gap
132
+ material.set_new_lattice_vectors(
133
+ lattice_vector1=new_lattice_vectors[0],
134
+ lattice_vector2=new_lattice_vectors[1],
135
+ lattice_vector3=new_lattice_vectors[2],
136
+ )
137
+ return material
138
+
139
+
140
+ def stack_two_materials_xy(
141
+ phase_1_material: Material,
142
+ phase_2_material: Material,
143
+ gap: float,
144
+ edge_inclusion_tolerance: Optional[float] = 1.0,
145
+ distance_tolerance: float = 1.0,
146
+ ) -> Material:
147
+ """
148
+ Stack two materials laterally with translation along x-axis with a gap between them.
149
+
150
+ Works correctly only for materials with the same lattice vectors (commensurate lattices).
151
+ Args:
152
+ phase_1_material (Material): The first material.
153
+ phase_2_material (Material): The second material.
154
+ gap (float): The gap between the two materials, in angstroms.
155
+ edge_inclusion_tolerance (float): The tolerance to include atoms on the edge of the phase, in angstroms.
156
+ distance_tolerance (float): The distance tolerance to remove atoms that are too close, in angstroms.
157
+
158
+ Returns:
159
+ Material: The merged material.
160
+ """
161
+ edge_inclusion_tolerance_crystal = abs(
162
+ phase_1_material.basis.cell.convert_point_to_crystal([edge_inclusion_tolerance, 0, 0])[0]
163
+ )
164
+
165
+ phase_1_material = double_and_filter_material(
166
+ phase_1_material, [0 - edge_inclusion_tolerance_crystal, 0, 0], [0.5 + edge_inclusion_tolerance_crystal, 1, 1]
167
+ )
168
+
169
+ phase_2_material = double_and_filter_material(
170
+ phase_2_material, [0.5 - edge_inclusion_tolerance_crystal, 0, 0], [1 + edge_inclusion_tolerance_crystal, 1, 1]
171
+ )
172
+
173
+ phase_1_material = expand_lattice_vectors(phase_1_material, gap)
174
+ phase_2_material = expand_lattice_vectors(phase_2_material, gap)
175
+
176
+ phase_2_material = translate_by_vector(phase_2_material, [gap / 2, 0, 0], use_cartesian_coordinates=True)
177
+ interface = merge_materials([phase_1_material, phase_2_material], distance_tolerance=distance_tolerance)
178
+ return interface
@@ -178,49 +178,101 @@ SI_SLAB_CONFIGURATION: Dict[str, Any] = {
178
178
  }
179
179
 
180
180
  SI_SLAB: Dict[str, Any] = {
181
+ "name": "Si8(001), termination Si_P6/mmm_1, Slab",
181
182
  "basis": {
182
- "elements": [
183
- {"id": 0, "value": "Si"},
184
- {"id": 1, "value": "Si"},
185
- {"id": 2, "value": "Si"},
186
- {"id": 3, "value": "Si"},
187
- ],
183
+ "elements": [{"id": 0, "value": "Si"}, {"id": 1, "value": "Si"}],
188
184
  "coordinates": [
189
- {"id": 0, "value": [0.5, 0.5, 0.5625]},
190
- {"id": 1, "value": [0.5, 0.0, 0.6875]},
191
- {"id": 2, "value": [0.0, 0.0, 0.8125]},
192
- {"id": 3, "value": [-0.0, 0.5, 0.9375]},
185
+ {"id": 0, "value": [0.583333333, 0.833333333, 0.241911889]},
186
+ {"id": 1, "value": [0.25, 0.5, 0.145147133]},
193
187
  ],
194
188
  "units": "crystal",
195
- "cell": [[3.867, 0.0, 0.0], [0.0, 3.867, 0.0], [0.0, 0.0, 10.937527692]],
189
+ "cell": [[3.867, 0.0, 0.0], [1.9335, 3.348920236, 0.0], [0.0, 0.0, 8.157392279]],
196
190
  "constraints": [],
197
191
  "labels": [],
198
192
  },
199
193
  "lattice": {
200
194
  "a": 3.867,
201
195
  "b": 3.867,
202
- "c": 10.937527692,
196
+ "c": 8.157392279,
203
197
  "alpha": 90.0,
204
198
  "beta": 90.0,
205
- "gamma": 90.0,
199
+ "gamma": 60.0,
206
200
  "units": {"length": "angstrom", "angle": "degree"},
207
201
  "type": "TRI",
208
202
  "vectors": {
209
203
  "a": [3.867, 0.0, 0.0],
210
- "b": [0.0, 3.867, 0.0],
211
- "c": [0.0, 0.0, 10.937527692],
204
+ "b": [1.9335, 3.348920236, 0.0],
205
+ "c": [0.0, 0.0, 8.157392279],
212
206
  "alat": 1,
213
207
  "units": "angstrom",
214
208
  },
215
209
  },
216
- "name": "Si8(001), termination Si_P4/mmm_1, Slab",
217
210
  "isNonPeriodic": False,
218
211
  "_id": "",
219
212
  "metadata": {
220
213
  "boundaryConditions": {"type": "pbc", "offset": 0},
221
214
  "build": {
222
- "configuration": SI_SLAB_CONFIGURATION,
223
- "termination": "Si_P4/mmm_1",
215
+ "termination": "Si_P6/mmm_1",
216
+ "configuration": {
217
+ "type": "SlabConfiguration",
218
+ "bulk": {
219
+ "name": "Si8",
220
+ "basis": {
221
+ "elements": [
222
+ {"id": 0, "value": "Si"},
223
+ {"id": 1, "value": "Si"},
224
+ {"id": 2, "value": "Si"},
225
+ {"id": 3, "value": "Si"},
226
+ {"id": 4, "value": "Si"},
227
+ {"id": 5, "value": "Si"},
228
+ {"id": 6, "value": "Si"},
229
+ {"id": 7, "value": "Si"},
230
+ ],
231
+ "coordinates": [
232
+ {"id": 0, "value": [0.5, 0.0, 0.0]},
233
+ {"id": 1, "value": [0.25, 0.25, 0.75]},
234
+ {"id": 2, "value": [0.5, 0.5, 0.5]},
235
+ {"id": 3, "value": [0.25, 0.75, 0.25]},
236
+ {"id": 4, "value": [0.0, 0.0, 0.5]},
237
+ {"id": 5, "value": [0.75, 0.25, 0.25]},
238
+ {"id": 6, "value": [0.0, 0.5, 0.0]},
239
+ {"id": 7, "value": [0.75, 0.75, 0.75]},
240
+ ],
241
+ "units": "crystal",
242
+ "cell": [[5.468763846, 0.0, 0.0], [-0.0, 5.468763846, 0.0], [0.0, 0.0, 5.468763846]],
243
+ "constraints": [],
244
+ "labels": [],
245
+ },
246
+ "lattice": {
247
+ "a": 5.468763846,
248
+ "b": 5.468763846,
249
+ "c": 5.468763846,
250
+ "alpha": 90.0,
251
+ "beta": 90.0,
252
+ "gamma": 90.0,
253
+ "units": {"length": "angstrom", "angle": "degree"},
254
+ "type": "TRI",
255
+ "vectors": {
256
+ "a": [5.468763846, 0.0, 0.0],
257
+ "b": [-0.0, 5.468763846, 0.0],
258
+ "c": [0.0, 0.0, 5.468763846],
259
+ "alat": 1,
260
+ "units": "angstrom",
261
+ },
262
+ },
263
+ "isNonPeriodic": False,
264
+ "_id": "",
265
+ "metadata": {"boundaryConditions": {"type": "pbc", "offset": 0}},
266
+ "isUpdated": True,
267
+ },
268
+ "miller_indices": (0, 0, 1),
269
+ "thickness": 1,
270
+ "vacuum": 5.0,
271
+ "xy_supercell_matrix": [[1, 0], [0, 1]],
272
+ "use_conventional_cell": True,
273
+ "use_orthogonal_z": True,
274
+ "make_primitive": True,
275
+ },
224
276
  },
225
277
  },
226
278
  "isUpdated": True,
@@ -228,41 +280,37 @@ SI_SLAB: Dict[str, Any] = {
228
280
 
229
281
 
230
282
  SI_SLAB_PASSIVATED = {
231
- "name": "Si8(001), termination Si_P4/mmm_1, Slab H-passivated",
283
+ "name": "Si8(001), termination Si_P6/mmm_1, Slab H-passivated",
232
284
  "basis": {
233
285
  "elements": [
234
286
  {"id": 0, "value": "Si"},
235
287
  {"id": 1, "value": "Si"},
236
- {"id": 2, "value": "Si"},
237
- {"id": 3, "value": "Si"},
238
- {"id": 4, "value": "H"},
239
- {"id": 5, "value": "H"},
288
+ {"id": 2, "value": "H"},
289
+ {"id": 3, "value": "H"},
240
290
  ],
241
291
  "coordinates": [
242
- {"id": 0, "value": [0.5, 0.5, 0.312499993]},
243
- {"id": 1, "value": [0.5, 0.0, 0.437499993]},
244
- {"id": 2, "value": [0.0, 0.0, 0.562499993]},
245
- {"id": 3, "value": [0.0, 0.5, 0.687499993]},
246
- {"id": 4, "value": [0.5, 0.5, 0.177186054]},
247
- {"id": 5, "value": [0.0, 0.5, 0.822813932]},
292
+ {"id": 0, "value": [0.583333333, 0.833333333, 0.548382368]},
293
+ {"id": 1, "value": [0.25, 0.5, 0.451617612]},
294
+ {"id": 2, "value": [0.25, 0.5, 0.270187076]},
295
+ {"id": 3, "value": [0.583333333, 0.833333333, 0.729812904]},
248
296
  ],
249
297
  "units": "crystal",
250
- "cell": [[3.867, 0.0, 0.0], [-0.0, 3.867, 0.0], [0.0, 0.0, 10.937528]],
298
+ "cell": [[3.867, 0.0, 0.0], [1.9335, 3.34892, 0.0], [0.0, 0.0, 8.157392]],
251
299
  "labels": [],
252
300
  },
253
301
  "lattice": {
254
302
  "a": 3.867,
255
303
  "b": 3.867,
256
- "c": 10.937527692,
304
+ "c": 8.157392279,
257
305
  "alpha": 90.0,
258
306
  "beta": 90.0,
259
- "gamma": 90.0,
307
+ "gamma": 60.0,
260
308
  "units": {"length": "angstrom", "angle": "degree"},
261
309
  "type": "TRI",
262
310
  "vectors": {
263
311
  "a": [3.867, 0.0, 0.0],
264
- "b": [-0.0, 3.867, 0.0],
265
- "c": [0.0, 0.0, 10.937527692],
312
+ "b": [1.9335, 3.348920236, 0.0],
313
+ "c": [0.0, 0.0, 8.157392279],
266
314
  "alat": 1,
267
315
  "units": "angstrom",
268
316
  },
@@ -279,7 +327,7 @@ SI_SLAB_PASSIVATED = {
279
327
  "bond_length": 1.48,
280
328
  "surface": "both",
281
329
  },
282
- "termination": "Si_P4/mmm_1",
330
+ "termination": "Si_P6/mmm_1",
283
331
  },
284
332
  },
285
333
  "isUpdated": True,
@@ -288,14 +336,12 @@ SI_SLAB_PASSIVATED = {
288
336
 
289
337
  SI_SLAB_VACUUM = copy.deepcopy(SI_SLAB)
290
338
  SI_SLAB_VACUUM["basis"]["coordinates"] = [
291
- {"id": 0, "value": [0.5, 0.5, 0.386029718]},
292
- {"id": 1, "value": [0.5, 0.0, 0.4718141]},
293
- {"id": 2, "value": [0.0, 0.0, 0.557598482]},
294
- {"id": 3, "value": [-0.0, 0.5, 0.643382864]},
339
+ {"id": 0, "value": [0.583333333, 0.833333333, 0.149981861]},
340
+ {"id": 1, "value": [0.25, 0.5, 0.089989116]},
295
341
  ]
296
- SI_SLAB_VACUUM["basis"]["cell"] = [[3.867, 0.0, 0.0], [-0.0, 3.867, 0.0], [0.0, 0.0, 15.937527692]]
297
- SI_SLAB_VACUUM["lattice"]["c"] = 15.937527692
298
- SI_SLAB_VACUUM["lattice"]["vectors"]["c"] = [0.0, 0.0, 15.937527692]
342
+ SI_SLAB_VACUUM["basis"]["cell"] = [[3.867, 0.0, 0.0], [1.9335, 3.348920236, 0.0], [0.0, 0.0, 13.157392279]]
343
+ SI_SLAB_VACUUM["lattice"]["c"] = 13.157392279
344
+ SI_SLAB_VACUUM["lattice"]["vectors"]["c"] = [0.0, 0.0, 13.157392279]
299
345
 
300
346
 
301
347
  clean_material = Material.create(Material.default_config)
@@ -82,7 +82,7 @@ def test_create_adatom():
82
82
  defect = create_slab_defect(configuration=configuration, builder=None)
83
83
 
84
84
  assert defect.basis.elements.values[-1] == "Si"
85
- assertion_utils.assert_deep_almost_equal([0.5, 0.5, 0.450843412], defect.basis.coordinates.values[-1])
85
+ assertion_utils.assert_deep_almost_equal([0.5, 0.5, 0.764102218], defect.basis.coordinates.values[-1])
86
86
 
87
87
 
88
88
  def test_create_adatom_equidistant():
@@ -94,9 +94,7 @@ def test_create_adatom_equidistant():
94
94
 
95
95
  assert defect.basis.elements.values[-1] == "Si"
96
96
  # We expect adatom to shift from provided position
97
- assertion_utils.assert_deep_almost_equal(
98
- [0.583333334, 0.458333333, 0.450843412], defect.basis.coordinates.values[-1]
99
- )
97
+ assertion_utils.assert_deep_almost_equal([0.5, 0.5, 0.764102218], defect.basis.coordinates.values[-1])
100
98
 
101
99
 
102
100
  def test_create_crystal_site_adatom():
@@ -109,7 +107,9 @@ def test_create_crystal_site_adatom():
109
107
 
110
108
  assert defect.basis.elements.values[-1] == "Si"
111
109
  assertion_utils.assert_deep_almost_equal(
112
- [0.083333333, 0.458333333, 0.352272727], defect.basis.coordinates.values[-1]
110
+ # TO pass on GHA
111
+ [0.083333333, 0.458333333, 0.561569594],
112
+ defect.basis.coordinates.values[-1],
113
113
  )
114
114
 
115
115
 
@@ -140,7 +140,7 @@ def test_create_terrace():
140
140
  number_of_added_layers=1,
141
141
  )
142
142
  new_slab = TerraceSlabDefectBuilder().get_material(configuration=config)
143
- assertion_utils.assert_deep_almost_equal([0.777786402, 0.5, 0.444543344], new_slab.basis.coordinates.values[42])
143
+ assertion_utils.assert_deep_almost_equal([0.777786396, 0.5, 0.414655236], new_slab.basis.coordinates.values[42])
144
144
 
145
145
 
146
146
  def test_create_defect_pair():
@@ -2,12 +2,17 @@ from mat3ra.made.material import Material
2
2
  from mat3ra.made.tools.build.grain_boundary import (
3
3
  SlabGrainBoundaryBuilder,
4
4
  SlabGrainBoundaryConfiguration,
5
+ SurfaceGrainBoundaryBuilder,
6
+ SurfaceGrainBoundaryBuilderParameters,
7
+ SurfaceGrainBoundaryConfiguration,
5
8
  create_grain_boundary,
6
9
  )
7
- from mat3ra.made.tools.build.interface import ZSLStrainMatchingInterfaceBuilderParameters
10
+ from mat3ra.made.tools.build.grain_boundary.builders import SlabGrainBoundaryBuilderParameters
8
11
  from mat3ra.made.tools.build.slab import SlabConfiguration, get_terminations
9
12
  from mat3ra.utils import assertion as assertion_utils
10
13
 
14
+ from .fixtures import GRAPHENE
15
+
11
16
 
12
17
  def test_slab_grain_boundary_builder():
13
18
  material = Material(Material.default_config)
@@ -44,16 +49,44 @@ def test_slab_grain_boundary_builder():
44
49
  slab_configuration=slab_config,
45
50
  )
46
51
 
47
- builder_params = ZSLStrainMatchingInterfaceBuilderParameters(max_area=50)
52
+ builder_params = SlabGrainBoundaryBuilderParameters()
48
53
  builder = SlabGrainBoundaryBuilder(build_parameters=builder_params)
49
54
  gb = create_grain_boundary(config, builder)
50
55
  expected_lattice_vectors = [
51
56
  [25.140673461, 0.0, 0.0],
52
57
  [0.0, 3.867, 0.0],
53
- [0.0, 0.0, 11.601],
58
+ [0.0, 0.0, 8.734],
54
59
  ]
55
- expected_coordinate_15 = [0.777190818, 0.0, 0.083333333]
60
+ # To pass on GHA
61
+ expected_coordinate_15 = [0.777190818, 0.0, 0.110688115]
56
62
 
57
63
  assert len(gb.basis.elements.values) == 32
58
64
  assertion_utils.assert_deep_almost_equal(expected_coordinate_15, gb.basis.coordinates.values[15])
59
65
  assertion_utils.assert_deep_almost_equal(expected_lattice_vectors, gb.lattice.vector_arrays)
66
+
67
+
68
+ def test_create_surface_grain_boundary():
69
+ config = SurfaceGrainBoundaryConfiguration(
70
+ film=Material(GRAPHENE),
71
+ twist_angle=13.0,
72
+ gap=2.0,
73
+ )
74
+
75
+ builder_params = SurfaceGrainBoundaryBuilderParameters(
76
+ max_repetition_int=5,
77
+ angle_tolerance=0.5,
78
+ return_first_match=True,
79
+ distance_tolerance=1.0,
80
+ )
81
+ builder = SurfaceGrainBoundaryBuilder(build_parameters=builder_params)
82
+
83
+ gb = builder.get_materials(config)
84
+
85
+ expected_cell_vectors = [
86
+ [23.509344266, 0.0, 0.0],
87
+ [5.377336066500001, 9.313819276550575, 0.0],
88
+ [0.0, 0.0, 20.0],
89
+ ]
90
+
91
+ assert len(gb) == 1
92
+ assertion_utils.assert_deep_almost_equal(expected_cell_vectors, gb[0].basis.cell.vectors_as_array)
@@ -9,8 +9,8 @@ from mat3ra.made.tools.build.interface import (
9
9
  create_interfaces,
10
10
  )
11
11
  from mat3ra.made.tools.build.interface.builders import (
12
- CommensurateLatticeInterfaceBuilder,
13
- CommensurateLatticeInterfaceBuilderParameters,
12
+ CommensurateLatticeTwistedInterfaceBuilder,
13
+ CommensurateLatticeTwistedInterfaceBuilderParameters,
14
14
  NanoRibbonTwistedInterfaceBuilder,
15
15
  NanoRibbonTwistedInterfaceConfiguration,
16
16
  TwistedInterfaceConfiguration,
@@ -72,10 +72,10 @@ def test_create_commensurate_supercell_twisted_interface():
72
72
  film = Material(GRAPHENE)
73
73
  substrate = Material(GRAPHENE)
74
74
  config = TwistedInterfaceConfiguration(film=film, substrate=substrate, twist_angle=13, distance_z=3.0)
75
- params = CommensurateLatticeInterfaceBuilderParameters(
75
+ params = CommensurateLatticeTwistedInterfaceBuilderParameters(
76
76
  max_repetition_int=5, angle_tolerance=0.5, return_first_match=True
77
77
  )
78
- builder = CommensurateLatticeInterfaceBuilder(build_parameters=params)
78
+ builder = CommensurateLatticeTwistedInterfaceBuilder(build_parameters=params)
79
79
  interfaces = builder.get_materials(config, post_process_parameters=config)
80
80
  assert len(interfaces) == 1
81
81
  interface = interfaces[0]
@@ -12,11 +12,12 @@ def test_build_slab():
12
12
  bulk=material,
13
13
  miller_indices=(0, 0, 1),
14
14
  thickness=1,
15
- vacuum=1,
15
+ vacuum=5.0,
16
16
  xy_supercell_matrix=[[1, 0], [0, 1]],
17
17
  use_orthogonal_z=True,
18
18
  make_primitive=True,
19
19
  )
20
20
  termination = get_terminations(slab_config)[0]
21
21
  slab = create_slab(slab_config, termination)
22
+ print(slab.to_json())
22
23
  assertion_utils.assert_deep_almost_equal(SI_SLAB, slab.to_json())
@@ -153,7 +153,7 @@ def test_add_vacuum():
153
153
 
154
154
  def test_remove_vacuum():
155
155
  material_with_vacuum = Material(SI_SLAB_VACUUM)
156
- vacuum = 6.836
156
+ vacuum = 7.368
157
157
  material_with_no_vacuum = remove_vacuum(material_with_vacuum, from_top=True, from_bottom=True, fixed_padding=0)
158
158
  material_with_set_vacuum = add_vacuum(material_with_no_vacuum, vacuum)
159
159
  # to compare correctly, we need to translate the expected material to the bottom