From e56404d150410d323a5cc452f7c91e768122a3eb Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:56:30 -0700 Subject: [PATCH 01/13] update: autodetect max int --- .../made/tools/build/interface/builders.py | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index f544b581..9fc2e8c9 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -270,36 +270,67 @@ class CommensurateLatticeTwistedInterfaceBuilder(BaseBuilder): def _generate(self, configuration: _ConfigurationType) -> List[_GeneratedItemType]: film = configuration.film # substrate = configuration.substrate - max_search = self.build_parameters.max_repetition_int a = film.lattice.vector_arrays[0][:2] b = film.lattice.vector_arrays[1][:2] + max_int = self.__determine_max_int(a, b, configuration.twist_angle) commensurate_lattice_pairs = self.__generate_commensurate_lattices( - configuration, a, b, max_search, configuration.twist_angle + configuration, a, b, max_int, configuration.twist_angle ) return commensurate_lattice_pairs + def __determine_max_int(self, a: List[float], b: List[float], target_angle: float) -> int: + """ + Determine the maximum integer for the transformation matrices based on the target angle. + + Args: + a (List[float]): The a lattice vector. + b (List[float]): The b lattice vector. + target_angle (float): The target twist angle, in degrees. + + Returns: + int: The maximum integer for the transformation matrices. + """ + # Compute the average length of the in-plane lattice vectors + length_a = np.linalg.norm(a) + length_b = np.linalg.norm(b) + average_length = (length_a + length_b) / 2 + + # Convert the target angle to radians + theta_rad = np.radians(target_angle) + + # Predict the necessary maximum integer for the transformation matrices + if theta_rad == 0: + max_int = self.build_parameters.max_repetition_int + else: + max_int = int(np.ceil(average_length / theta_rad)) + + # Optional: Limit max_int to a predefined maximum + max_int = min(max_int, self.build_parameters.max_repetition_int) + return max_int + def __generate_commensurate_lattices( self, configuration: TwistedInterfaceConfiguration, a: List[float], b: List[float], - max_search: int = 10, + max_int: int, target_angle: float = 0.0, ) -> List[CommensurateLatticePair]: """ - Generate all commensurate lattices for a given search range and filter by closeness to target angle. + Generate all commensurate lattices for a given target angle and filter by closeness to target angle. Args: configuration (TwistedInterfaceConfiguration): The configuration for the twisted interface. a (List[float]): The a lattice vector. b (List[float]): The b lattice vector. - max_search (int): The maximum search range. - target_angle (float): The target angle, in degrees. + max_int (int): The maximum integer for the transformation matrices. + target_angle (float): The target twist angle, in degrees. Returns: List[CommensurateLatticePair]: The list of commensurate lattice pairs """ - matrices = create_2d_supercell_matrices(max_search) + # Generate the supercell matrices using the calculated max_int + matrices = create_2d_supercell_matrices(max_int) matrix_ab = np.array([a, b]) matrix_ab_inverse = np.linalg.inv(matrix_ab) @@ -342,7 +373,7 @@ def _post_process( new_film = translate_by_vector( new_film, [0, 0, item.configuration.distance_z], use_cartesian_coordinates=True ) - interface = merge_materials([new_substrate, new_film]) + interface = merge_materials([new_substrate, new_film], merge_dangerously=True) interface.metadata["actual_twist_angle"] = item.angle interfaces.append(interface) return interfaces From 43f116beabbf5cbb252796f00a0714d8dd46639e Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:13:21 -0700 Subject: [PATCH 02/13] chore: prune comments --- src/py/mat3ra/made/tools/build/interface/builders.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index 9fc2e8c9..3f362718 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -290,22 +290,17 @@ def __determine_max_int(self, a: List[float], b: List[float], target_angle: floa Returns: int: The maximum integer for the transformation matrices. """ - # Compute the average length of the in-plane lattice vectors length_a = np.linalg.norm(a) length_b = np.linalg.norm(b) average_length = (length_a + length_b) / 2 - - # Convert the target angle to radians theta_rad = np.radians(target_angle) - # Predict the necessary maximum integer for the transformation matrices if theta_rad == 0: max_int = self.build_parameters.max_repetition_int else: max_int = int(np.ceil(average_length / theta_rad)) - # Optional: Limit max_int to a predefined maximum - max_int = min(max_int, self.build_parameters.max_repetition_int) + max_int = max(max_int, self.build_parameters.max_repetition_int) return max_int def __generate_commensurate_lattices( From a498c607e6e5e7a654cba142430eb4cd6ea0aea5 Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Wed, 30 Oct 2024 13:55:53 -0700 Subject: [PATCH 03/13] update: rerun with higher max int if not found --- .../made/tools/build/interface/builders.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index 3f362718..84110702 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -252,13 +252,14 @@ class CommensurateLatticeTwistedInterfaceBuilderParameters(BaseModel): Parameters for the commensurate lattice interface builder. Args: - max_repetition_int (int): The maximum search range for commensurate lattices. + max_repetition_int (Optional[int]): The maximum integer for the transformation matrices. If not provided, it + will be determined based on the target angle and the lattice vectors automatically. angle_tolerance (float): The tolerance for the angle between the commensurate lattices and the target angle, in degrees. return_first_match (bool): Whether to return the first match or all matches. """ - max_repetition_int: int = 10 + max_repetition_int: Optional[int] = None angle_tolerance: float = 0.1 return_first_match: bool = False @@ -272,10 +273,14 @@ def _generate(self, configuration: _ConfigurationType) -> List[_GeneratedItemTyp # substrate = configuration.substrate a = film.lattice.vector_arrays[0][:2] b = film.lattice.vector_arrays[1][:2] - max_int = self.__determine_max_int(a, b, configuration.twist_angle) - commensurate_lattice_pairs = self.__generate_commensurate_lattices( - configuration, a, b, max_int, configuration.twist_angle - ) + max_int = self.build_parameters.max_repetition_int or self.__determine_max_int(a, b, configuration.twist_angle) + commensurate_lattice_pairs: List[CommensurateLatticePair] = [] + while not commensurate_lattice_pairs: + commensurate_lattice_pairs = self.__generate_commensurate_lattices( + configuration, a, b, max_int, configuration.twist_angle + ) + max_int += 1 + return commensurate_lattice_pairs def __determine_max_int(self, a: List[float], b: List[float], target_angle: float) -> int: @@ -296,11 +301,9 @@ def __determine_max_int(self, a: List[float], b: List[float], target_angle: floa theta_rad = np.radians(target_angle) if theta_rad == 0: - max_int = self.build_parameters.max_repetition_int + max_int = 1 else: max_int = int(np.ceil(average_length / theta_rad)) - - max_int = max(max_int, self.build_parameters.max_repetition_int) return max_int def __generate_commensurate_lattices( From 4428963174a53426cde58ca941791afd959f1568 Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:34:42 -0700 Subject: [PATCH 04/13] update: adjuste name for twisted interfaces --- src/py/mat3ra/made/tools/build/interface/builders.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index 84110702..f4fba72a 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -243,7 +243,7 @@ def _generate(self, configuration: _ConfigurationType) -> List[Material]: def _update_material_name( self, material: Material, configuration: NanoRibbonTwistedInterfaceConfiguration ) -> Material: - material.name = f"Twisted Nanoribbon Interface ({configuration.twist_angle:.2f}°)" + material.name = f"Twisted Nanoribbon Interface ({configuration.twist_angle:.2f} degrees)" return material @@ -383,3 +383,9 @@ def _update_material_metadata(self, material, configuration) -> Material: "actual_twist_angle" ] return updated_material + + def _update_material_name( + self, material: Material, configuration: NanoRibbonTwistedInterfaceConfiguration + ) -> Material: + material.name = f"Twisted Bilayer Interface ({configuration.actual_twist_angle:.2f} degrees)" + return material From da73c3ae9fec4b6592e464892635c0d46352a842 Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:39:43 -0700 Subject: [PATCH 05/13] chore: remove a mistake --- src/py/mat3ra/made/tools/build/interface/builders.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index f4fba72a..16dec959 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -387,5 +387,5 @@ def _update_material_metadata(self, material, configuration) -> Material: def _update_material_name( self, material: Material, configuration: NanoRibbonTwistedInterfaceConfiguration ) -> Material: - material.name = f"Twisted Bilayer Interface ({configuration.actual_twist_angle:.2f} degrees)" + material.name = f"Twisted Bilayer Interface ({material.metadata['actual_twist_angle']:.2f} degrees)" return material From cee4a64a469edf60175e4ca31bb4b06fc2af3dcf Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:18:57 -0700 Subject: [PATCH 06/13] update: add vacuum to twisted interface --- src/py/mat3ra/made/tools/build/interface/builders.py | 3 +++ src/py/mat3ra/made/tools/build/interface/configuration.py | 1 + 2 files changed, 4 insertions(+) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index 16dec959..14888bc5 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -15,6 +15,7 @@ rotate, translate_by_vector, add_vacuum_sides, + add_vacuum, ) from ...analyze import get_chemical_formula from ...convert import to_ase, from_ase, to_pymatgen, PymatgenInterface, ASEAtoms @@ -373,6 +374,8 @@ def _post_process( ) interface = merge_materials([new_substrate, new_film], merge_dangerously=True) interface.metadata["actual_twist_angle"] = item.angle + if item.configuration.vacuum != 0: + interface = add_vacuum(interface, item.configuration.vacuum) interfaces.append(interface) return interfaces diff --git a/src/py/mat3ra/made/tools/build/interface/configuration.py b/src/py/mat3ra/made/tools/build/interface/configuration.py index 5269c7aa..9f863566 100644 --- a/src/py/mat3ra/made/tools/build/interface/configuration.py +++ b/src/py/mat3ra/made/tools/build/interface/configuration.py @@ -40,6 +40,7 @@ class TwistedInterfaceConfiguration(BaseConfiguration): substrate: Optional[Material] = None twist_angle: float = 0.0 distance_z: float = 3.0 + vacuum: float = 0.0 @property def _json(self): From e99cb5d091b95fe9712d95075067758feb6c69fa Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Fri, 1 Nov 2024 20:57:37 -0700 Subject: [PATCH 07/13] wip: tabulate angles for hex --- .../made/tools/build/interface/builders.py | 13 +++++- .../made/tools/build/interface/enums.py | 45 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index 14888bc5..b88341af 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -274,7 +274,7 @@ def _generate(self, configuration: _ConfigurationType) -> List[_GeneratedItemTyp # substrate = configuration.substrate a = film.lattice.vector_arrays[0][:2] b = film.lattice.vector_arrays[1][:2] - max_int = self.build_parameters.max_repetition_int or self.__determine_max_int(a, b, configuration.twist_angle) + max_int = self.build_parameters.max_repetition_int or self.__determine_max_int(film, configuration.twist_angle) commensurate_lattice_pairs: List[CommensurateLatticePair] = [] while not commensurate_lattice_pairs: commensurate_lattice_pairs = self.__generate_commensurate_lattices( @@ -284,7 +284,7 @@ def _generate(self, configuration: _ConfigurationType) -> List[_GeneratedItemTyp return commensurate_lattice_pairs - def __determine_max_int(self, a: List[float], b: List[float], target_angle: float) -> int: + def __determine_max_int(self, film, target_angle: float) -> int: """ Determine the maximum integer for the transformation matrices based on the target angle. @@ -296,13 +296,22 @@ def __determine_max_int(self, a: List[float], b: List[float], target_angle: floa Returns: int: The maximum integer for the transformation matrices. """ + a = film.lattice.vector_arrays[0][:2] + b = film.lattice.vector_arrays[1][:2] length_a = np.linalg.norm(a) length_b = np.linalg.norm(b) average_length = (length_a + length_b) / 2 theta_rad = np.radians(target_angle) + # Factor in both the lattice length and twist angle, ensuring a minimum of 1 for very small angles if theta_rad == 0: max_int = 1 + if film.lattice.type == "HEX": + from .enums import angle_to_lmpq + + closest_match = min(angle_to_lmpq, key=lambda x: abs(x[0] - target_angle)) + l, m, p, q = closest_match[1] # type: ignore + max_int = max(l, m, p, q) else: max_int = int(np.ceil(average_length / theta_rad)) return max_int diff --git a/src/py/mat3ra/made/tools/build/interface/enums.py b/src/py/mat3ra/made/tools/build/interface/enums.py index 364d19b2..4e209059 100644 --- a/src/py/mat3ra/made/tools/build/interface/enums.py +++ b/src/py/mat3ra/made/tools/build/interface/enums.py @@ -10,3 +10,48 @@ class StrainModes(str, Enum): class SupercellTypes(str, Enum): hexagonal = "hexagonal" orthogonal = "orthogonal" + + +angle_to_lmpq = [ + (60.0, [0, 1, -1, 1]), + (21.7867892983, [1, 2, -2, 3]), + (13.1735511073, [2, 3, -3, 5]), + (9.4300079079, [3, 4, -4, 7]), + (7.34099301663, [4, 5, -5, 9]), + (6.00898319777, [5, 6, -6, 11]), + (5.08584780812, [6, 7, -7, 13]), + (4.40845500794, [7, 8, -8, 15]), + (3.89023816901, [8, 9, -9, 17]), + (3.48100608947, [9, 10, -10, 19]), + (3.14965742639, [10, 11, -11, 21]), + (2.87589463363, [11, 12, -12, 23]), + (2.64590838119, [12, 13, -13, 25]), + (2.44997727662, [13, 14, -14, 27]), + (2.28105960973, [14, 15, -15, 29]), + (2.1339296666, [15, 16, -16, 31]), + (2.00462783069, [16, 17, -17, 33]), + (1.89009907347, [17, 18, -18, 35]), + (1.78794861038, [18, 19, -19, 37]), + (1.69627269352, [19, 20, -20, 39]), + (1.61353890116, [20, 21, -21, 41]), + (1.53849981837, [21, 22, -22, 43]), + (1.47012972578, [22, 23, -23, 45]), + (1.40757744635, [23, 24, -24, 47]), + (1.35013073557, [24, 25, -25, 49]), + (1.29718904759, [25, 26, -26, 51]), + (1.24824246553, [26, 27, -27, 53]), + (1.20285522748, [27, 28, -28, 55]), + (1.16065271985, [28, 29, -29, 57]), + (1.12131111538, [29, 30, -30, 59]), + (1.08454904916, [30, 31, -31, 61]), + (1.05012087979, [31, 32, -32, 63]), + (1.01781119445, [32, 33, -33, 65]), + (0.987430297814, [33, 34, -34, 67]), + (0.958810485525, [34, 35, -35, 69]), + (0.931802947264, [35, 36, -36, 71]), + (0.906275178895, [36, 37, -37, 73]), + (0.882108808579, [37, 38, -38, 75]), + (0.859197761631, [38, 39, -39, 77]), + (0.8374467041, [39, 40, -40, 79]), + (0.816769716893, [40, 41, -41, 81]), +] From 3ab3e7901a1de0b3447edf338a2fbe3e1d074fa0 Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Mon, 4 Nov 2024 10:53:53 -0800 Subject: [PATCH 08/13] chore: small adjustments and renames --- .../mat3ra/made/tools/build/interface/builders.py | 15 ++++++++------- src/py/mat3ra/made/tools/build/interface/enums.py | 3 ++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index b88341af..0177cc74 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -274,9 +274,11 @@ def _generate(self, configuration: _ConfigurationType) -> List[_GeneratedItemTyp # substrate = configuration.substrate a = film.lattice.vector_arrays[0][:2] b = film.lattice.vector_arrays[1][:2] - max_int = self.build_parameters.max_repetition_int or self.__determine_max_int(film, configuration.twist_angle) + max_int = self.build_parameters.max_repetition_int or self.__get_initial_guess_for_max_int( + film, configuration.twist_angle + ) commensurate_lattice_pairs: List[CommensurateLatticePair] = [] - while not commensurate_lattice_pairs: + while not commensurate_lattice_pairs and max_int < 42: commensurate_lattice_pairs = self.__generate_commensurate_lattices( configuration, a, b, max_int, configuration.twist_angle ) @@ -284,7 +286,7 @@ def _generate(self, configuration: _ConfigurationType) -> List[_GeneratedItemTyp return commensurate_lattice_pairs - def __determine_max_int(self, film, target_angle: float) -> int: + def __get_initial_guess_for_max_int(self, film, target_angle: float) -> int: """ Determine the maximum integer for the transformation matrices based on the target angle. @@ -307,11 +309,10 @@ def __determine_max_int(self, film, target_angle: float) -> int: if theta_rad == 0: max_int = 1 if film.lattice.type == "HEX": - from .enums import angle_to_lmpq + from .enums import angle_to_lmpq_hex - closest_match = min(angle_to_lmpq, key=lambda x: abs(x[0] - target_angle)) - l, m, p, q = closest_match[1] # type: ignore - max_int = max(l, m, p, q) + closest_match = min(angle_to_lmpq_hex, key=lambda x: abs(x[0] - target_angle)) + max_int = max(closest_match[1]) else: max_int = int(np.ceil(average_length / theta_rad)) return max_int diff --git a/src/py/mat3ra/made/tools/build/interface/enums.py b/src/py/mat3ra/made/tools/build/interface/enums.py index 4e209059..e77df631 100644 --- a/src/py/mat3ra/made/tools/build/interface/enums.py +++ b/src/py/mat3ra/made/tools/build/interface/enums.py @@ -12,7 +12,8 @@ class SupercellTypes(str, Enum): orthogonal = "orthogonal" -angle_to_lmpq = [ +# Tabulation from https://github.com/qtm-iisc/Twister/blob/474156a2a59f2b9d59350b32de56864a9496f848/examples/Homobilayer_hex/hex.table +angle_to_lmpq_hex = [ (60.0, [0, 1, -1, 1]), (21.7867892983, [1, 2, -2, 3]), (13.1735511073, [2, 3, -3, 5]), From 5f88ce0f0aa96fe8063fa01e783f1279438004aa Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:32:40 -0800 Subject: [PATCH 09/13] update: renames and remove unclear --- .../made/tools/build/interface/builders.py | 43 ++++++++----------- .../made/tools/build/interface/enums.py | 2 +- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index 0177cc74..4d96f081 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -29,7 +29,7 @@ ConvertGeneratedItemsPymatgenStructureMixin, ) -from .enums import StrainModes +from .enums import StrainModes, angle_to_supercell_matrix_values_for_hex from .configuration import ( InterfaceConfiguration, NanoRibbonTwistedInterfaceConfiguration, @@ -253,14 +253,16 @@ class CommensurateLatticeTwistedInterfaceBuilderParameters(BaseModel): Parameters for the commensurate lattice interface builder. Args: - max_repetition_int (Optional[int]): The maximum integer for the transformation matrices. If not provided, it - will be determined based on the target angle and the lattice vectors automatically. + max_supercell_matrix_int (Optional[int]): The maximum integer for the transformation matrices. + If not provided, it will be determined based on the target angle and the lattice vectors automatically. + limit_max_int (Optional[int]): The limit for the maximum integer for the transformation matrices when searching angle_tolerance (float): The tolerance for the angle between the commensurate lattices and the target angle, in degrees. return_first_match (bool): Whether to return the first match or all matches. """ - max_repetition_int: Optional[int] = None + max_supercell_matrix_int: Optional[int] = None + limit_max_int: Optional[int] = 42 angle_tolerance: float = 0.1 return_first_match: bool = False @@ -274,11 +276,12 @@ def _generate(self, configuration: _ConfigurationType) -> List[_GeneratedItemTyp # substrate = configuration.substrate a = film.lattice.vector_arrays[0][:2] b = film.lattice.vector_arrays[1][:2] - max_int = self.build_parameters.max_repetition_int or self.__get_initial_guess_for_max_int( + max_int = self.build_parameters.max_supercell_matrix_int or self.__get_initial_guess_for_max_int( film, configuration.twist_angle ) commensurate_lattice_pairs: List[CommensurateLatticePair] = [] - while not commensurate_lattice_pairs and max_int < 42: + while not commensurate_lattice_pairs and max_int < self.build_parameters.limit_max_int: + print(f"Trying max_int = {max_int}") commensurate_lattice_pairs = self.__generate_commensurate_lattices( configuration, a, b, max_int, configuration.twist_angle ) @@ -298,23 +301,13 @@ def __get_initial_guess_for_max_int(self, film, target_angle: float) -> int: Returns: int: The maximum integer for the transformation matrices. """ - a = film.lattice.vector_arrays[0][:2] - b = film.lattice.vector_arrays[1][:2] - length_a = np.linalg.norm(a) - length_b = np.linalg.norm(b) - average_length = (length_a + length_b) / 2 - theta_rad = np.radians(target_angle) - - # Factor in both the lattice length and twist angle, ensuring a minimum of 1 for very small angles - if theta_rad == 0: + if np.isclose(target_angle, 0.0, atol=0.01): max_int = 1 if film.lattice.type == "HEX": - from .enums import angle_to_lmpq_hex - - closest_match = min(angle_to_lmpq_hex, key=lambda x: abs(x[0] - target_angle)) - max_int = max(closest_match[1]) - else: - max_int = int(np.ceil(average_length / theta_rad)) + tabulated_matrix_match_for_given_angle = min( + angle_to_supercell_matrix_values_for_hex, key=lambda x: abs(x[0] - target_angle) + ) + max_int = max(tabulated_matrix_match_for_given_angle[1]) return max_int def __generate_commensurate_lattices( @@ -322,7 +315,7 @@ def __generate_commensurate_lattices( configuration: TwistedInterfaceConfiguration, a: List[float], b: List[float], - max_int: int, + max_supercell_matrix_element_int: int, target_angle: float = 0.0, ) -> List[CommensurateLatticePair]: """ @@ -332,14 +325,14 @@ def __generate_commensurate_lattices( configuration (TwistedInterfaceConfiguration): The configuration for the twisted interface. a (List[float]): The a lattice vector. b (List[float]): The b lattice vector. - max_int (int): The maximum integer for the transformation matrices. + max_supercell_matrix_element_int (int): The maximum integer for the transformation matrices. target_angle (float): The target twist angle, in degrees. Returns: List[CommensurateLatticePair]: The list of commensurate lattice pairs """ - # Generate the supercell matrices using the calculated max_int - matrices = create_2d_supercell_matrices(max_int) + # Generate the supercell matrices using the calculated max_supercell_matrix_element_int + matrices = create_2d_supercell_matrices(max_supercell_matrix_element_int) matrix_ab = np.array([a, b]) matrix_ab_inverse = np.linalg.inv(matrix_ab) diff --git a/src/py/mat3ra/made/tools/build/interface/enums.py b/src/py/mat3ra/made/tools/build/interface/enums.py index e77df631..2f62e46f 100644 --- a/src/py/mat3ra/made/tools/build/interface/enums.py +++ b/src/py/mat3ra/made/tools/build/interface/enums.py @@ -13,7 +13,7 @@ class SupercellTypes(str, Enum): # Tabulation from https://github.com/qtm-iisc/Twister/blob/474156a2a59f2b9d59350b32de56864a9496f848/examples/Homobilayer_hex/hex.table -angle_to_lmpq_hex = [ +angle_to_supercell_matrix_values_for_hex = [ (60.0, [0, 1, -1, 1]), (21.7867892983, [1, 2, -2, 3]), (13.1735511073, [2, 3, -3, 5]), From 357bca33e585f394d6e90c60fe47403c2a457efe Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:37:16 -0800 Subject: [PATCH 10/13] update: simplify --- src/py/mat3ra/made/tools/build/interface/builders.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index 4d96f081..8736e1f3 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -301,14 +301,12 @@ def __get_initial_guess_for_max_int(self, film, target_angle: float) -> int: Returns: int: The maximum integer for the transformation matrices. """ - if np.isclose(target_angle, 0.0, atol=0.01): - max_int = 1 if film.lattice.type == "HEX": tabulated_matrix_match_for_given_angle = min( angle_to_supercell_matrix_values_for_hex, key=lambda x: abs(x[0] - target_angle) ) - max_int = max(tabulated_matrix_match_for_given_angle[1]) - return max_int + return max(tabulated_matrix_match_for_given_angle[1]) + return 1 def __generate_commensurate_lattices( self, From 69acbd44a3c6fb37e5babdfa40133fb894c89daf Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:16:27 -0800 Subject: [PATCH 11/13] update: add case for 0 degrees --- src/py/mat3ra/made/tools/build/interface/enums.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/py/mat3ra/made/tools/build/interface/enums.py b/src/py/mat3ra/made/tools/build/interface/enums.py index 2f62e46f..96d149cc 100644 --- a/src/py/mat3ra/made/tools/build/interface/enums.py +++ b/src/py/mat3ra/made/tools/build/interface/enums.py @@ -55,4 +55,5 @@ class SupercellTypes(str, Enum): (0.859197761631, [38, 39, -39, 77]), (0.8374467041, [39, 40, -40, 79]), (0.816769716893, [40, 41, -41, 81]), + (0.0, [1, 0, 0, 1]), ] From 356e51b23a2415e5f8315d534a3853ec3705b8bd Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Tue, 5 Nov 2024 21:16:51 -0800 Subject: [PATCH 12/13] chore: turn angle-matrix into map --- .../made/tools/build/interface/builders.py | 6 +- .../made/tools/build/interface/enums.py | 85 ++++++++++--------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index 8736e1f3..3f302f14 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -302,10 +302,10 @@ def __get_initial_guess_for_max_int(self, film, target_angle: float) -> int: int: The maximum integer for the transformation matrices. """ if film.lattice.type == "HEX": - tabulated_matrix_match_for_given_angle = min( - angle_to_supercell_matrix_values_for_hex, key=lambda x: abs(x[0] - target_angle) + xy_supercell_matrix_for_closest_angle = min( + angle_to_supercell_matrix_values_for_hex, key=lambda x: abs(float(x["angle"]) - target_angle) ) - return max(tabulated_matrix_match_for_given_angle[1]) + return max(**xy_supercell_matrix_for_closest_angle["xy_supercell"]) return 1 def __generate_commensurate_lattices( diff --git a/src/py/mat3ra/made/tools/build/interface/enums.py b/src/py/mat3ra/made/tools/build/interface/enums.py index 96d149cc..d19fe43e 100644 --- a/src/py/mat3ra/made/tools/build/interface/enums.py +++ b/src/py/mat3ra/made/tools/build/interface/enums.py @@ -13,47 +13,48 @@ class SupercellTypes(str, Enum): # Tabulation from https://github.com/qtm-iisc/Twister/blob/474156a2a59f2b9d59350b32de56864a9496f848/examples/Homobilayer_hex/hex.table +# Maps twist angle to supercell matrix values for homo-material hexagonal supercells bilayer angle_to_supercell_matrix_values_for_hex = [ - (60.0, [0, 1, -1, 1]), - (21.7867892983, [1, 2, -2, 3]), - (13.1735511073, [2, 3, -3, 5]), - (9.4300079079, [3, 4, -4, 7]), - (7.34099301663, [4, 5, -5, 9]), - (6.00898319777, [5, 6, -6, 11]), - (5.08584780812, [6, 7, -7, 13]), - (4.40845500794, [7, 8, -8, 15]), - (3.89023816901, [8, 9, -9, 17]), - (3.48100608947, [9, 10, -10, 19]), - (3.14965742639, [10, 11, -11, 21]), - (2.87589463363, [11, 12, -12, 23]), - (2.64590838119, [12, 13, -13, 25]), - (2.44997727662, [13, 14, -14, 27]), - (2.28105960973, [14, 15, -15, 29]), - (2.1339296666, [15, 16, -16, 31]), - (2.00462783069, [16, 17, -17, 33]), - (1.89009907347, [17, 18, -18, 35]), - (1.78794861038, [18, 19, -19, 37]), - (1.69627269352, [19, 20, -20, 39]), - (1.61353890116, [20, 21, -21, 41]), - (1.53849981837, [21, 22, -22, 43]), - (1.47012972578, [22, 23, -23, 45]), - (1.40757744635, [23, 24, -24, 47]), - (1.35013073557, [24, 25, -25, 49]), - (1.29718904759, [25, 26, -26, 51]), - (1.24824246553, [26, 27, -27, 53]), - (1.20285522748, [27, 28, -28, 55]), - (1.16065271985, [28, 29, -29, 57]), - (1.12131111538, [29, 30, -30, 59]), - (1.08454904916, [30, 31, -31, 61]), - (1.05012087979, [31, 32, -32, 63]), - (1.01781119445, [32, 33, -33, 65]), - (0.987430297814, [33, 34, -34, 67]), - (0.958810485525, [34, 35, -35, 69]), - (0.931802947264, [35, 36, -36, 71]), - (0.906275178895, [36, 37, -37, 73]), - (0.882108808579, [37, 38, -38, 75]), - (0.859197761631, [38, 39, -39, 77]), - (0.8374467041, [39, 40, -40, 79]), - (0.816769716893, [40, 41, -41, 81]), - (0.0, [1, 0, 0, 1]), + {"angle": 60.0, "xy_supercell": [[0, 1], [-1, 1]]}, + {"angle": 21.7867892983, "xy_supercell": [[1, 2], [-2, 3]]}, + {"angle": 13.1735511073, "xy_supercell": [[2, 3], [-3, 5]]}, + {"angle": 9.4300079079, "xy_supercell": [[3, 4], [-4, 7]]}, + {"angle": 7.34099301663, "xy_supercell": [[4, 5], [-5, 9]]}, + {"angle": 6.00898319777, "xy_supercell": [[5, 6], [-6, 11]]}, + {"angle": 5.08584780812, "xy_supercell": [[6, 7], [-7, 13]]}, + {"angle": 4.40845500794, "xy_supercell": [[7, 8], [-8, 15]]}, + {"angle": 3.89023816901, "xy_supercell": [[8, 9], [-9, 17]]}, + {"angle": 3.48100608947, "xy_supercell": [[9, 10], [-10, 19]]}, + {"angle": 3.14965742639, "xy_supercell": [[10, 11], [-11, 21]]}, + {"angle": 2.87589463363, "xy_supercell": [[11, 12], [-12, 23]]}, + {"angle": 2.64590838119, "xy_supercell": [[12, 13], [-13, 25]]}, + {"angle": 2.44997727662, "xy_supercell": [[13, 14], [-14, 27]]}, + {"angle": 2.28105960973, "xy_supercell": [[14, 15], [-15, 29]]}, + {"angle": 2.1339296666, "xy_supercell": [[15, 16], [-16, 31]]}, + {"angle": 2.00462783069, "xy_supercell": [[16, 17], [-17, 33]]}, + {"angle": 1.89009907347, "xy_supercell": [[17, 18], [-18, 35]]}, + {"angle": 1.78794861038, "xy_supercell": [[18, 19], [-19, 37]]}, + {"angle": 1.69627269352, "xy_supercell": [[19, 20], [-20, 39]]}, + {"angle": 1.61353890116, "xy_supercell": [[20, 21], [-21, 41]]}, + {"angle": 1.53849981837, "xy_supercell": [[21, 22], [-22, 43]]}, + {"angle": 1.47012972578, "xy_supercell": [[22, 23], [-23, 45]]}, + {"angle": 1.40757744635, "xy_supercell": [[23, 24], [-24, 47]]}, + {"angle": 1.35013073557, "xy_supercell": [[24, 25], [-25, 49]]}, + {"angle": 1.29718904759, "xy_supercell": [[25, 26], [-26, 51]]}, + {"angle": 1.24824246553, "xy_supercell": [[26, 27], [-27, 53]]}, + {"angle": 1.20285522748, "xy_supercell": [[27, 28], [-28, 55]]}, + {"angle": 1.16065271985, "xy_supercell": [[28, 29], [-29, 57]]}, + {"angle": 1.12131111538, "xy_supercell": [[29, 30], [-30, 59]]}, + {"angle": 1.08454904916, "xy_supercell": [[30, 31], [-31, 61]]}, + {"angle": 1.05012087979, "xy_supercell": [[31, 32], [-32, 63]]}, + {"angle": 1.01781119445, "xy_supercell": [[32, 33], [-33, 65]]}, + {"angle": 0.987430297814, "xy_supercell": [[33, 34], [-34, 67]]}, + {"angle": 0.958810485525, "xy_supercell": [[34, 35], [-35, 69]]}, + {"angle": 0.931802947264, "xy_supercell": [[35, 36], [-36, 71]]}, + {"angle": 0.906275178895, "xy_supercell": [[36, 37], [-37, 73]]}, + {"angle": 0.882108808579, "xy_supercell": [[37, 38], [-38, 75]]}, + {"angle": 0.859197761631, "xy_supercell": [[38, 39], [-39, 77]]}, + {"angle": 0.8374467041, "xy_supercell": [[39, 40], [-40, 79]]}, + {"angle": 0.816769716893, "xy_supercell": [[40, 41], [-41, 81]]}, + {"angle": 0.0, "xy_supercell": [[1, 0], [0, 1]]}, ] From 75ac32169f9f7ed1a44cc0057faeffde45796950 Mon Sep 17 00:00:00 2001 From: VsevolodX <79542055+VsevolodX@users.noreply.github.com> Date: Tue, 5 Nov 2024 21:26:18 -0800 Subject: [PATCH 13/13] update: fix error --- src/py/mat3ra/made/tools/build/interface/builders.py | 6 ++++-- src/py/mat3ra/made/tools/build/interface/enums.py | 8 +++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/py/mat3ra/made/tools/build/interface/builders.py b/src/py/mat3ra/made/tools/build/interface/builders.py index 3f302f14..2c72c1c6 100644 --- a/src/py/mat3ra/made/tools/build/interface/builders.py +++ b/src/py/mat3ra/made/tools/build/interface/builders.py @@ -302,10 +302,12 @@ def __get_initial_guess_for_max_int(self, film, target_angle: float) -> int: int: The maximum integer for the transformation matrices. """ if film.lattice.type == "HEX": + # getting max int of the matrix that has angle closest to target angle xy_supercell_matrix_for_closest_angle = min( - angle_to_supercell_matrix_values_for_hex, key=lambda x: abs(float(x["angle"]) - target_angle) + angle_to_supercell_matrix_values_for_hex, key=lambda x: abs(x["angle"] - target_angle) ) - return max(**xy_supercell_matrix_for_closest_angle["xy_supercell"]) + # Get maximum absolute value from the supercell matrix values + return max(abs(x) for row in xy_supercell_matrix_for_closest_angle["xy_supercell"] for x in row) return 1 def __generate_commensurate_lattices( diff --git a/src/py/mat3ra/made/tools/build/interface/enums.py b/src/py/mat3ra/made/tools/build/interface/enums.py index d19fe43e..7a776006 100644 --- a/src/py/mat3ra/made/tools/build/interface/enums.py +++ b/src/py/mat3ra/made/tools/build/interface/enums.py @@ -1,4 +1,5 @@ from enum import Enum +from typing import TypedDict, List class StrainModes(str, Enum): @@ -12,9 +13,14 @@ class SupercellTypes(str, Enum): orthogonal = "orthogonal" +class SupercellMatrix(TypedDict): + angle: float + xy_supercell: List[List[int]] + + # Tabulation from https://github.com/qtm-iisc/Twister/blob/474156a2a59f2b9d59350b32de56864a9496f848/examples/Homobilayer_hex/hex.table # Maps twist angle to supercell matrix values for homo-material hexagonal supercells bilayer -angle_to_supercell_matrix_values_for_hex = [ +angle_to_supercell_matrix_values_for_hex: List[SupercellMatrix] = [ {"angle": 60.0, "xy_supercell": [[0, 1], [-1, 1]]}, {"angle": 21.7867892983, "xy_supercell": [[1, 2], [-2, 3]]}, {"angle": 13.1735511073, "xy_supercell": [[2, 3], [-3, 5]]},