Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/SOF-7433 feat: add grain boundary #161

Merged
merged 32 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5c7234e
feat: add gb configuration
VsevolodX Sep 7, 2024
9b4c86e
feat: first implementation of slab grain boundary
VsevolodX Sep 7, 2024
af92c2f
chore: run lint fix
VsevolodX Sep 7, 2024
59d90c6
update: add crucial control over selection in base builder
VsevolodX Sep 7, 2024
d4a5354
update: use OOP beauty to remove duplication
VsevolodX Sep 7, 2024
5e955df
update: structure folders as builder
VsevolodX Sep 7, 2024
69e2ee4
update: add top level function to create gb
VsevolodX Sep 7, 2024
30981a6
chore: run lint fix and types
VsevolodX Sep 7, 2024
153ed93
chore: add docstring
VsevolodX Sep 7, 2024
6b651e1
update: various fixes for types and imports
VsevolodX Sep 23, 2024
d36a741
chore: types and pydantic
VsevolodX Sep 24, 2024
aa3ece9
update: fix default values usage
VsevolodX Sep 24, 2024
e5e881c
Merge branch 'main' into feature/SOF-7433
VsevolodX Sep 24, 2024
a18918c
update: move rotation down the chain
VsevolodX Sep 24, 2024
a47b537
update: working code -- forgoes slab creation in favor of supercell w…
VsevolodX Sep 24, 2024
0be8666
update: fix an error
VsevolodX Sep 25, 2024
13bac90
chore: docstring
VsevolodX Sep 25, 2024
72c9bdd
chore: docstring 2
VsevolodX Sep 25, 2024
1ed2a98
update: add test for slab gb -- wip
VsevolodX Sep 26, 2024
115aefb
update: return correct creation of slab from interface in gb
VsevolodX Sep 26, 2024
9de7611
chore: imports
VsevolodX Sep 26, 2024
4efc52a
update: fix test
VsevolodX Sep 26, 2024
fafdbd8
update: add more assertions to test
VsevolodX Sep 26, 2024
ad2e4c2
chore: round to 0
VsevolodX Sep 26, 2024
3010b5a
chore: rename gb -> slabgb
VsevolodX Sep 26, 2024
89e8f9b
chore: update test
VsevolodX Sep 26, 2024
c55a200
try: types
VsevolodX Sep 26, 2024
da17d90
try: types analogous to slab builder
VsevolodX Sep 26, 2024
7bbdcef
try: debug print
VsevolodX Sep 26, 2024
62e0cee
try: invariant config
VsevolodX Sep 26, 2024
7e79fe9
try: pass gha
VsevolodX Sep 26, 2024
d297b18
Merge branch 'main' into feature/SOF-7433
VsevolodX Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/py/mat3ra/made/tools/build/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ def _json(self):
raise NotImplementedError


class BaseSelectorParameters(BaseModel):
default_index: int = 0


class BaseBuilderParameters(BaseModel):
class Config:
arbitrary_types_allowed = True


class BaseBuilder(BaseModel):
"""
Base class for material builders.
Expand Down Expand Up @@ -56,6 +65,7 @@ class BaseBuilder(BaseModel):
_GeneratedItemType: Any = Any
_SelectorParametersType: Any = None
_PostProcessParametersType: Any = None
selector_parameters: Any = BaseSelectorParameters()

def __init__(self, build_parameters: _BuildParametersType = None):
super().__init__(build_parameters=build_parameters)
Expand Down Expand Up @@ -115,7 +125,9 @@ def get_material(
selector_parameters: Optional[_SelectorParametersType] = None,
post_process_parameters: Optional[_PostProcessParametersType] = None,
) -> Material:
return self.get_materials(configuration, selector_parameters, post_process_parameters)[0]
return self.get_materials(configuration, selector_parameters, post_process_parameters)[
self.selector_parameters.default_index
]

def _update_material_name(self, material, configuration):
# Do nothing by default
Expand Down
15 changes: 15 additions & 0 deletions src/py/mat3ra/made/tools/build/grain_boundary/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from typing import Optional

from mat3ra.made.material import Material

from .builders import GrainBoundaryBuilder
from .configuration import GrainBoundaryConfiguration


def create_grain_boundary(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring

configuration: GrainBoundaryConfiguration,
builder: Optional[GrainBoundaryBuilder] = None,
) -> Material:
if builder is None:
builder = GrainBoundaryBuilder()
return builder.get_material(configuration)
84 changes: 84 additions & 0 deletions src/py/mat3ra/made/tools/build/grain_boundary/builders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from typing import List

from mat3ra.made.material import Material

from ...analyze import get_chemical_formula
from ..interface import ZSLStrainMatchingInterfaceBuilderParameters, InterfaceConfiguration
from ..interface.builders import ZSLStrainMatchingInterfaceBuilder
from ..supercell import create_supercell
from ..slab import SlabConfiguration, get_terminations, SlabBuilder, SlabSelectorParameters
from .configuration import GrainBoundaryConfiguration


class GrainBoundaryBuilderParameters(ZSLStrainMatchingInterfaceBuilderParameters):
selected_interface_index: int = 0


class GrainBoundaryBuilder(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(GrainBoundaryBuilderParameters) = GrainBoundaryBuilderParameters # type: ignore
_ConfigurationType: type(GrainBoundaryConfiguration) = GrainBoundaryConfiguration # type: ignore
_GeneratedItemType: type(Material) = Material # type: ignore
selector_parameters: type( # type: ignore
GrainBoundaryBuilderParameters
) = GrainBoundaryBuilderParameters().selected_interface_index # type: ignore

def _generate(self, configuration: _ConfigurationType) -> List[Material]:
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,
)
interfaces = super()._generate(interface_config)
rot_90_degree_matrix = [[0, 1, 0], [-1, 0, 0], [0, 0, 1]]
rotated_interfaces = [
create_supercell(interface, xy_supercell_matrix=rot_90_degree_matrix) for interface in interfaces
]
return rotated_interfaces

def _finalize(self, materials: List[Material], configuration: _ConfigurationType) -> List[Material]:
final_slabs = []
for interface in materials:
slab_config = SlabConfiguration(
bulk=interface,
miller_indices=configuration.slab_configuration.miller_indices,
thickness=configuration.slab_configuration.thickness,
vacuum=configuration.slab_configuration.vacuum,
xy_supercell_matrix=configuration.slab_configuration.xy_supercell_matrix,
use_conventional_cell=True,
use_orthogonal_z=True,
)
slab_builder = SlabBuilder()
slab_termination = (
configuration.slab_termination if configuration.slab_termination else get_terminations(slab_config)[0]
)
final_slab = slab_builder.get_material(
configuration,
selector_parameters=SlabSelectorParameters(termination=slab_termination),
)
final_slabs.append(final_slab)

materials = super()._finalize(final_slabs, configuration)
return materials

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
41 changes: 41 additions & 0 deletions src/py/mat3ra/made/tools/build/grain_boundary/configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from typing import Optional

from .. import BaseConfiguration
from ..slab.configuration import SlabConfiguration
from ..slab.termination import Termination


class GrainBoundaryConfiguration(BaseConfiguration):
"""
Configuration for a grain boundary in a slab material.

Attributes:
phase_1_configuration: SlabConfiguration
phase_2_configuration: SlabConfiguration
phase_1_termination: Termination
phase_2_termination: Termination
gap: float
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need units

slab_configuration: SlabConfiguration
slab_termination: Termination

"""

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]

@property
def _json(self):
return {
"type": "GrainBoundaryConfiguration",
"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(),
}
6 changes: 3 additions & 3 deletions src/py/mat3ra/made/tools/build/interface/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from ..slab.configuration import SlabConfiguration
from ...analyze import get_chemical_formula
from ...convert import to_ase, from_ase, to_pymatgen, PymatgenInterface, ASEAtoms
from ...build import BaseBuilder
from ...build import BaseBuilder, BaseBuilderParameters


class InterfaceBuilderParameters(BaseModel):
Expand Down Expand Up @@ -106,7 +106,7 @@ def _post_process(self, items: List[_GeneratedItemType], post_process_parameters
########################################################################################
# Strain Matching Interface Builders #
########################################################################################
class StrainMatchingInterfaceBuilderParameters(BaseModel):
class StrainMatchingInterfaceBuilderParameters(BaseBuilderParameters):
strain_matching_parameters: Optional[Any] = None


Expand All @@ -130,7 +130,7 @@ class ZSLStrainMatchingParameters(BaseModel):


class ZSLStrainMatchingInterfaceBuilderParameters(StrainMatchingInterfaceBuilderParameters):
strain_matching_parameters: ZSLStrainMatchingParameters
strain_matching_parameters: ZSLStrainMatchingParameters = ZSLStrainMatchingParameters()


class ZSLStrainMatchingInterfaceBuilder(ConvertGeneratedItemsPymatgenStructureMixin, StrainMatchingInterfaceBuilder):
Expand Down
7 changes: 3 additions & 4 deletions src/py/mat3ra/made/tools/build/slab/configuration.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
from typing import List, Tuple, Any

import numpy as np
from pydantic import BaseModel

from mat3ra.code.entity import InMemoryEntity

from mat3ra.made.material import Material

from .. import BaseConfiguration
from ...third_party import PymatgenSpacegroupAnalyzer
from ...convert import to_pymatgen, from_pymatgen


class SlabConfiguration(BaseModel, InMemoryEntity):
class SlabConfiguration(BaseConfiguration):
"""
Configuration for building a slab.

Expand Down
Loading