-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #161 from Exabyte-io/feature/SOF-7433
feature/SOF-7433 feat: add grain boundary
- Loading branch information
Showing
7 changed files
with
225 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from typing import Optional | ||
|
||
from mat3ra.made.material import Material | ||
|
||
from .builders import SlabGrainBoundaryBuilder | ||
from .configuration import SlabGrainBoundaryConfiguration | ||
|
||
|
||
def create_grain_boundary( | ||
configuration: SlabGrainBoundaryConfiguration, | ||
builder: Optional[SlabGrainBoundaryBuilder] = None, | ||
) -> Material: | ||
""" | ||
Create a grain boundary according to provided configuration with selected builder. | ||
Args: | ||
configuration (SlabGrainBoundaryConfiguration): The configuration of the grain boundary. | ||
builder (Optional[SlabGrainBoundaryBuilder]): The builder to use for creating the grain boundary. | ||
Returns: | ||
Material: The material with the grain boundary. | ||
""" | ||
if builder is None: | ||
builder = SlabGrainBoundaryBuilder() | ||
return builder.get_material(configuration) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
from typing import List | ||
|
||
import numpy as np | ||
from mat3ra.made.material import Material | ||
|
||
from ..slab import SlabConfiguration, get_terminations, create_slab | ||
from ...analyze import get_chemical_formula | ||
from ..interface import ZSLStrainMatchingInterfaceBuilderParameters, InterfaceConfiguration | ||
from ..interface.builders import ZSLStrainMatchingInterfaceBuilder | ||
from ..supercell import create_supercell | ||
from .configuration import SlabGrainBoundaryConfiguration | ||
from ...third_party import PymatgenInterface | ||
|
||
|
||
class SlabGrainBoundaryBuilderParameters(ZSLStrainMatchingInterfaceBuilderParameters): | ||
default_index: int = 0 | ||
|
||
|
||
class SlabGrainBoundaryBuilder(ZSLStrainMatchingInterfaceBuilder): | ||
""" | ||
A builder for creating grain boundaries. | ||
The grain boundary is created by: | ||
1. creating an interface between two phases, | ||
2. then rotating the interface by 90 degrees. | ||
3. Finally, creating a slab from the rotated interface. | ||
""" | ||
|
||
_BuildParametersType: type(SlabGrainBoundaryBuilderParameters) = SlabGrainBoundaryBuilderParameters # type: ignore | ||
_ConfigurationType: type(SlabGrainBoundaryConfiguration) = SlabGrainBoundaryConfiguration # type: ignore | ||
_GeneratedItemType: PymatgenInterface = PymatgenInterface # type: ignore | ||
selector_parameters: type( # type: ignore | ||
SlabGrainBoundaryBuilderParameters | ||
) = SlabGrainBoundaryBuilderParameters() # type: ignore | ||
|
||
def _generate(self, configuration: _ConfigurationType) -> List[_GeneratedItemType]: # type: ignore | ||
interface_config = InterfaceConfiguration( | ||
film_configuration=configuration.phase_1_configuration, | ||
substrate_configuration=configuration.phase_2_configuration, | ||
film_termination=configuration.phase_1_termination, | ||
substrate_termination=configuration.phase_2_termination, | ||
distance_z=configuration.gap, | ||
vacuum=configuration.gap, | ||
) | ||
return super()._generate(interface_config) | ||
|
||
def _finalize(self, materials: List[Material], configuration: _ConfigurationType) -> List[Material]: | ||
rot_90_degree_matrix = [[0, 0, 1], [0, 1, 0], [-1, 0, 0]] | ||
rotated_interfaces = [ | ||
create_supercell(material, supercell_matrix=rot_90_degree_matrix) for material in materials | ||
] | ||
final_slabs: List[Material] = [] | ||
for interface in rotated_interfaces: | ||
supercell_matrix = np.zeros((3, 3)) | ||
supercell_matrix[:2, :2] = configuration.slab_configuration.xy_supercell_matrix | ||
supercell_matrix[2, 2] = configuration.slab_configuration.thickness | ||
final_slab_config = SlabConfiguration( | ||
bulk=interface, | ||
vacuum=configuration.slab_configuration.vacuum, | ||
miller_indices=configuration.slab_configuration.miller_indices, | ||
thickness=configuration.slab_configuration.thickness, | ||
use_conventional_cell=False, # Keep false to prevent Pymatgen from simplifying the interface | ||
use_orthogonal_z=True, | ||
) | ||
termination = configuration.slab_termination or get_terminations(final_slab_config)[0] | ||
final_slab = create_slab(final_slab_config, termination) | ||
final_slabs.append(final_slab) | ||
|
||
return super()._finalize(final_slabs, configuration) | ||
|
||
def _update_material_name(self, material: Material, configuration: _ConfigurationType) -> Material: | ||
phase_1_formula = get_chemical_formula(configuration.phase_1_configuration.bulk) | ||
phase_2_formula = get_chemical_formula(configuration.phase_2_configuration.bulk) | ||
phase_1_miller_indices = "".join([str(i) for i in configuration.phase_1_configuration.miller_indices]) | ||
phase_2_miller_indices = "".join([str(i) for i in configuration.phase_2_configuration.miller_indices]) | ||
new_name = ( | ||
f"{phase_1_formula}({phase_1_miller_indices})-{phase_2_formula}({phase_2_miller_indices}), Grain Boundary" | ||
) | ||
material.name = new_name | ||
return material |
40 changes: 40 additions & 0 deletions
40
src/py/mat3ra/made/tools/build/grain_boundary/configuration.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from typing import Optional | ||
|
||
from .. import BaseConfiguration | ||
from ..slab.configuration import SlabConfiguration | ||
from ..slab.termination import Termination | ||
|
||
|
||
class SlabGrainBoundaryConfiguration(BaseConfiguration): | ||
""" | ||
Configuration for a grain boundary between two phases with different surfaces facing each other. | ||
Attributes: | ||
phase_1_configuration (SlabConfiguration): The configuration of the first phase. | ||
phase_2_configuration (SlabConfiguration): The configuration of the second phase. | ||
phase_1_termination (Termination): The termination of the first phase. | ||
phase_2_termination (Termination): The termination of the second phase. | ||
gap (float): The gap between the two phases, in Angstroms. | ||
slab_configuration (SlabConfiguration): The configuration of the grain boundary slab. | ||
slab_termination (Optional[Termination]): The termination of the grain boundary slab. | ||
""" | ||
|
||
phase_1_configuration: SlabConfiguration | ||
phase_2_configuration: SlabConfiguration | ||
phase_1_termination: Termination | ||
phase_2_termination: Termination | ||
gap: float = 3.0 | ||
slab_configuration: SlabConfiguration | ||
slab_termination: Optional[Termination] = None | ||
|
||
@property | ||
def _json(self): | ||
return { | ||
"type": self.__class__.__name__, | ||
"phase_1_configuration": self.phase_1_configuration.to_json(), | ||
"phase_2_configuration": self.phase_2_configuration.to_json(), | ||
"phase_1_termination": str(self.phase_1_termination), | ||
"phase_2_termination": str(self.phase_2_termination), | ||
"gap": self.gap, | ||
"slab_configuration": self.slab_configuration.to_json(), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from mat3ra.made.material import Material | ||
from mat3ra.made.tools.build.grain_boundary import ( | ||
SlabGrainBoundaryBuilder, | ||
SlabGrainBoundaryConfiguration, | ||
create_grain_boundary, | ||
) | ||
from mat3ra.made.tools.build.interface import ZSLStrainMatchingInterfaceBuilderParameters | ||
from mat3ra.made.tools.build.slab import SlabConfiguration, get_terminations | ||
from mat3ra.utils import assertion as assertion_utils | ||
|
||
|
||
def test_slab_grain_boundary_builder(): | ||
material = Material(Material.default_config) | ||
phase_1_configuration = SlabConfiguration( | ||
bulk=material, | ||
vacuum=0, | ||
thickness=2, | ||
miller_indices=(0, 0, 1), | ||
) | ||
|
||
phase_2_configuration = SlabConfiguration( | ||
bulk=material, | ||
vacuum=0, | ||
thickness=2, | ||
miller_indices=(0, 0, 1), | ||
) | ||
|
||
termination1 = get_terminations(phase_1_configuration)[0] | ||
termination2 = get_terminations(phase_2_configuration)[0] | ||
|
||
slab_config = SlabConfiguration( | ||
vacuum=1, | ||
miller_indices=(0, 0, 1), | ||
thickness=2, | ||
xy_supercell_matrix=[[1, 0], [0, 1]], | ||
) | ||
|
||
config = SlabGrainBoundaryConfiguration( | ||
phase_1_configuration=phase_1_configuration, | ||
phase_2_configuration=phase_2_configuration, | ||
phase_1_termination=termination1, | ||
phase_2_termination=termination2, | ||
gap=3.0, | ||
slab_configuration=slab_config, | ||
) | ||
|
||
builder_params = ZSLStrainMatchingInterfaceBuilderParameters(max_area=50) | ||
builder = SlabGrainBoundaryBuilder(build_parameters=builder_params) | ||
gb = create_grain_boundary(config, builder) | ||
expected_lattice_vectors = [ | ||
[25.140673461, 0.0, 0.0], | ||
[0.0, 3.867, 0.0], | ||
[0.0, 0.0, 11.601], | ||
] | ||
expected_coordinate_15 = [0.777190818, 0.0, 0.083333333] | ||
|
||
assert len(gb.basis.elements.values) == 32 | ||
assertion_utils.assert_deep_almost_equal(expected_coordinate_15, gb.basis.coordinates.values[15]) | ||
assertion_utils.assert_deep_almost_equal(expected_lattice_vectors, gb.lattice.vector_arrays) |