@mat3ra/made 2024.11.1-0 → 2024.11.7-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.11.1-0",
3
+ "version": "2024.11.7-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",
@@ -80,11 +80,19 @@ def create_slab_defect(
80
80
 
81
81
  Args:
82
82
  configuration: The configuration of the defect to be added.
83
- builder: The builder to be used to create the defect.
83
+ builder: Optional builder to be used to create the defect.
84
+ If None, builder will be selected based on configuration.
84
85
 
85
86
  Returns:
86
87
  The material with the defect added.
87
88
  """
88
89
  if builder is None:
89
- builder = AdatomSlabDefectBuilder(build_parameters=SlabDefectBuilderParameters())
90
+ if configuration.defect_type == PointDefectTypeEnum.ADATOM:
91
+ builder_key = f"adatom:{configuration.placement_method.lower()}"
92
+ else:
93
+ builder_key = configuration.defect_type.lower()
94
+
95
+ BuilderClass = DefectBuilderFactory.get_class_by_name(builder_key)
96
+ builder = BuilderClass(build_parameters=SlabDefectBuilderParameters())
97
+
90
98
  return builder.get_material(configuration)
@@ -15,6 +15,7 @@ from ...modify import (
15
15
  rotate,
16
16
  translate_by_vector,
17
17
  add_vacuum_sides,
18
+ add_vacuum,
18
19
  )
19
20
  from ...analyze import get_chemical_formula
20
21
  from ...convert import to_ase, from_ase, to_pymatgen, PymatgenInterface, ASEAtoms
@@ -28,7 +29,7 @@ from ..mixins import (
28
29
  ConvertGeneratedItemsPymatgenStructureMixin,
29
30
  )
30
31
 
31
- from .enums import StrainModes
32
+ from .enums import StrainModes, angle_to_supercell_matrix_values_for_hex
32
33
  from .configuration import (
33
34
  InterfaceConfiguration,
34
35
  NanoRibbonTwistedInterfaceConfiguration,
@@ -243,7 +244,7 @@ class NanoRibbonTwistedInterfaceBuilder(BaseBuilder):
243
244
  def _update_material_name(
244
245
  self, material: Material, configuration: NanoRibbonTwistedInterfaceConfiguration
245
246
  ) -> Material:
246
- material.name = f"Twisted Nanoribbon Interface ({configuration.twist_angle:.2f}°)"
247
+ material.name = f"Twisted Nanoribbon Interface ({configuration.twist_angle:.2f} degrees)"
247
248
  return material
248
249
 
249
250
 
@@ -252,13 +253,16 @@ class CommensurateLatticeTwistedInterfaceBuilderParameters(BaseModel):
252
253
  Parameters for the commensurate lattice interface builder.
253
254
 
254
255
  Args:
255
- max_repetition_int (int): The maximum search range for commensurate lattices.
256
+ max_supercell_matrix_int (Optional[int]): The maximum integer for the transformation matrices.
257
+ If not provided, it will be determined based on the target angle and the lattice vectors automatically.
258
+ limit_max_int (Optional[int]): The limit for the maximum integer for the transformation matrices when searching
256
259
  angle_tolerance (float): The tolerance for the angle between the commensurate lattices
257
260
  and the target angle, in degrees.
258
261
  return_first_match (bool): Whether to return the first match or all matches.
259
262
  """
260
263
 
261
- max_repetition_int: int = 10
264
+ max_supercell_matrix_int: Optional[int] = None
265
+ limit_max_int: Optional[int] = 42
262
266
  angle_tolerance: float = 0.1
263
267
  return_first_match: bool = False
264
268
 
@@ -270,36 +274,65 @@ class CommensurateLatticeTwistedInterfaceBuilder(BaseBuilder):
270
274
  def _generate(self, configuration: _ConfigurationType) -> List[_GeneratedItemType]:
271
275
  film = configuration.film
272
276
  # substrate = configuration.substrate
273
- max_search = self.build_parameters.max_repetition_int
274
277
  a = film.lattice.vector_arrays[0][:2]
275
278
  b = film.lattice.vector_arrays[1][:2]
276
- commensurate_lattice_pairs = self.__generate_commensurate_lattices(
277
- configuration, a, b, max_search, configuration.twist_angle
279
+ max_int = self.build_parameters.max_supercell_matrix_int or self.__get_initial_guess_for_max_int(
280
+ film, configuration.twist_angle
278
281
  )
282
+ commensurate_lattice_pairs: List[CommensurateLatticePair] = []
283
+ while not commensurate_lattice_pairs and max_int < self.build_parameters.limit_max_int:
284
+ print(f"Trying max_int = {max_int}")
285
+ commensurate_lattice_pairs = self.__generate_commensurate_lattices(
286
+ configuration, a, b, max_int, configuration.twist_angle
287
+ )
288
+ max_int += 1
289
+
279
290
  return commensurate_lattice_pairs
280
291
 
292
+ def __get_initial_guess_for_max_int(self, film, target_angle: float) -> int:
293
+ """
294
+ Determine the maximum integer for the transformation matrices based on the target angle.
295
+
296
+ Args:
297
+ a (List[float]): The a lattice vector.
298
+ b (List[float]): The b lattice vector.
299
+ target_angle (float): The target twist angle, in degrees.
300
+
301
+ Returns:
302
+ int: The maximum integer for the transformation matrices.
303
+ """
304
+ if film.lattice.type == "HEX":
305
+ # getting max int of the matrix that has angle closest to target angle
306
+ xy_supercell_matrix_for_closest_angle = min(
307
+ angle_to_supercell_matrix_values_for_hex, key=lambda x: abs(x["angle"] - target_angle)
308
+ )
309
+ # Get maximum absolute value from the supercell matrix values
310
+ return max(abs(x) for row in xy_supercell_matrix_for_closest_angle["xy_supercell"] for x in row)
311
+ return 1
312
+
281
313
  def __generate_commensurate_lattices(
282
314
  self,
283
315
  configuration: TwistedInterfaceConfiguration,
284
316
  a: List[float],
285
317
  b: List[float],
286
- max_search: int = 10,
318
+ max_supercell_matrix_element_int: int,
287
319
  target_angle: float = 0.0,
288
320
  ) -> List[CommensurateLatticePair]:
289
321
  """
290
- Generate all commensurate lattices for a given search range and filter by closeness to target angle.
322
+ Generate all commensurate lattices for a given target angle and filter by closeness to target angle.
291
323
 
292
324
  Args:
293
325
  configuration (TwistedInterfaceConfiguration): The configuration for the twisted interface.
294
326
  a (List[float]): The a lattice vector.
295
327
  b (List[float]): The b lattice vector.
296
- max_search (int): The maximum search range.
297
- target_angle (float): The target angle, in degrees.
328
+ max_supercell_matrix_element_int (int): The maximum integer for the transformation matrices.
329
+ target_angle (float): The target twist angle, in degrees.
298
330
 
299
331
  Returns:
300
332
  List[CommensurateLatticePair]: The list of commensurate lattice pairs
301
333
  """
302
- matrices = create_2d_supercell_matrices(max_search)
334
+ # Generate the supercell matrices using the calculated max_supercell_matrix_element_int
335
+ matrices = create_2d_supercell_matrices(max_supercell_matrix_element_int)
303
336
  matrix_ab = np.array([a, b])
304
337
  matrix_ab_inverse = np.linalg.inv(matrix_ab)
305
338
 
@@ -342,8 +375,10 @@ class CommensurateLatticeTwistedInterfaceBuilder(BaseBuilder):
342
375
  new_film = translate_by_vector(
343
376
  new_film, [0, 0, item.configuration.distance_z], use_cartesian_coordinates=True
344
377
  )
345
- interface = merge_materials([new_substrate, new_film])
378
+ interface = merge_materials([new_substrate, new_film], merge_dangerously=True)
346
379
  interface.metadata["actual_twist_angle"] = item.angle
380
+ if item.configuration.vacuum != 0:
381
+ interface = add_vacuum(interface, item.configuration.vacuum)
347
382
  interfaces.append(interface)
348
383
  return interfaces
349
384
 
@@ -354,3 +389,9 @@ class CommensurateLatticeTwistedInterfaceBuilder(BaseBuilder):
354
389
  "actual_twist_angle"
355
390
  ]
356
391
  return updated_material
392
+
393
+ def _update_material_name(
394
+ self, material: Material, configuration: NanoRibbonTwistedInterfaceConfiguration
395
+ ) -> Material:
396
+ material.name = f"Twisted Bilayer Interface ({material.metadata['actual_twist_angle']:.2f} degrees)"
397
+ return material
@@ -40,6 +40,7 @@ class TwistedInterfaceConfiguration(BaseConfiguration):
40
40
  substrate: Optional[Material] = None
41
41
  twist_angle: float = 0.0
42
42
  distance_z: float = 3.0
43
+ vacuum: float = 0.0
43
44
 
44
45
  @property
45
46
  def _json(self):
@@ -1,4 +1,5 @@
1
1
  from enum import Enum
2
+ from typing import TypedDict, List
2
3
 
3
4
 
4
5
  class StrainModes(str, Enum):
@@ -10,3 +11,56 @@ class StrainModes(str, Enum):
10
11
  class SupercellTypes(str, Enum):
11
12
  hexagonal = "hexagonal"
12
13
  orthogonal = "orthogonal"
14
+
15
+
16
+ class SupercellMatrix(TypedDict):
17
+ angle: float
18
+ xy_supercell: List[List[int]]
19
+
20
+
21
+ # Tabulation from https://github.com/qtm-iisc/Twister/blob/474156a2a59f2b9d59350b32de56864a9496f848/examples/Homobilayer_hex/hex.table
22
+ # Maps twist angle to supercell matrix values for homo-material hexagonal supercells bilayer
23
+ angle_to_supercell_matrix_values_for_hex: List[SupercellMatrix] = [
24
+ {"angle": 60.0, "xy_supercell": [[0, 1], [-1, 1]]},
25
+ {"angle": 21.7867892983, "xy_supercell": [[1, 2], [-2, 3]]},
26
+ {"angle": 13.1735511073, "xy_supercell": [[2, 3], [-3, 5]]},
27
+ {"angle": 9.4300079079, "xy_supercell": [[3, 4], [-4, 7]]},
28
+ {"angle": 7.34099301663, "xy_supercell": [[4, 5], [-5, 9]]},
29
+ {"angle": 6.00898319777, "xy_supercell": [[5, 6], [-6, 11]]},
30
+ {"angle": 5.08584780812, "xy_supercell": [[6, 7], [-7, 13]]},
31
+ {"angle": 4.40845500794, "xy_supercell": [[7, 8], [-8, 15]]},
32
+ {"angle": 3.89023816901, "xy_supercell": [[8, 9], [-9, 17]]},
33
+ {"angle": 3.48100608947, "xy_supercell": [[9, 10], [-10, 19]]},
34
+ {"angle": 3.14965742639, "xy_supercell": [[10, 11], [-11, 21]]},
35
+ {"angle": 2.87589463363, "xy_supercell": [[11, 12], [-12, 23]]},
36
+ {"angle": 2.64590838119, "xy_supercell": [[12, 13], [-13, 25]]},
37
+ {"angle": 2.44997727662, "xy_supercell": [[13, 14], [-14, 27]]},
38
+ {"angle": 2.28105960973, "xy_supercell": [[14, 15], [-15, 29]]},
39
+ {"angle": 2.1339296666, "xy_supercell": [[15, 16], [-16, 31]]},
40
+ {"angle": 2.00462783069, "xy_supercell": [[16, 17], [-17, 33]]},
41
+ {"angle": 1.89009907347, "xy_supercell": [[17, 18], [-18, 35]]},
42
+ {"angle": 1.78794861038, "xy_supercell": [[18, 19], [-19, 37]]},
43
+ {"angle": 1.69627269352, "xy_supercell": [[19, 20], [-20, 39]]},
44
+ {"angle": 1.61353890116, "xy_supercell": [[20, 21], [-21, 41]]},
45
+ {"angle": 1.53849981837, "xy_supercell": [[21, 22], [-22, 43]]},
46
+ {"angle": 1.47012972578, "xy_supercell": [[22, 23], [-23, 45]]},
47
+ {"angle": 1.40757744635, "xy_supercell": [[23, 24], [-24, 47]]},
48
+ {"angle": 1.35013073557, "xy_supercell": [[24, 25], [-25, 49]]},
49
+ {"angle": 1.29718904759, "xy_supercell": [[25, 26], [-26, 51]]},
50
+ {"angle": 1.24824246553, "xy_supercell": [[26, 27], [-27, 53]]},
51
+ {"angle": 1.20285522748, "xy_supercell": [[27, 28], [-28, 55]]},
52
+ {"angle": 1.16065271985, "xy_supercell": [[28, 29], [-29, 57]]},
53
+ {"angle": 1.12131111538, "xy_supercell": [[29, 30], [-30, 59]]},
54
+ {"angle": 1.08454904916, "xy_supercell": [[30, 31], [-31, 61]]},
55
+ {"angle": 1.05012087979, "xy_supercell": [[31, 32], [-32, 63]]},
56
+ {"angle": 1.01781119445, "xy_supercell": [[32, 33], [-33, 65]]},
57
+ {"angle": 0.987430297814, "xy_supercell": [[33, 34], [-34, 67]]},
58
+ {"angle": 0.958810485525, "xy_supercell": [[34, 35], [-35, 69]]},
59
+ {"angle": 0.931802947264, "xy_supercell": [[35, 36], [-36, 71]]},
60
+ {"angle": 0.906275178895, "xy_supercell": [[36, 37], [-37, 73]]},
61
+ {"angle": 0.882108808579, "xy_supercell": [[37, 38], [-38, 75]]},
62
+ {"angle": 0.859197761631, "xy_supercell": [[38, 39], [-39, 77]]},
63
+ {"angle": 0.8374467041, "xy_supercell": [[39, 40], [-40, 79]]},
64
+ {"angle": 0.816769716893, "xy_supercell": [[40, 41], [-41, 81]]},
65
+ {"angle": 0.0, "xy_supercell": [[1, 0], [0, 1]]},
66
+ ]