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 7397 #151

Merged
merged 59 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
4f049c6
feat: add first implementations of perturbation
VsevolodX Jul 25, 2024
af0f656
update: rename to deformation
VsevolodX Jul 26, 2024
15a99d0
update: rename to deformation
VsevolodX Jul 26, 2024
20b79c3
feat: add deformation function builder
VsevolodX Jul 26, 2024
7279daa
update: prepare for continuos deformation
VsevolodX Jul 26, 2024
6939b39
feat: working implementation
VsevolodX Jul 26, 2024
95337d6
update: add sine and radial sine
VsevolodX Jul 27, 2024
b53ce87
update: add sine helpers
VsevolodX Jul 27, 2024
50658ad
update: use sine helpers
VsevolodX Jul 27, 2024
b90b7ab
update: helps with second time interface creation
VsevolodX Jul 27, 2024
71987fb
Merge branch 'feature/SOF-7395' into feature/SOF-7397
VsevolodX Jul 30, 2024
11db833
update: simply use continuous deformations
VsevolodX Jul 31, 2024
b49ca69
update: optimize
VsevolodX Jul 31, 2024
f304dcb
update: types, cleanup
VsevolodX Jul 31, 2024
a97b7c3
Merge branch 'main' into feature/SOF-7397
VsevolodX Jul 31, 2024
9e98b26
update: fix and wrap
VsevolodX Aug 1, 2024
425892d
update: move functions to holder
VsevolodX Aug 1, 2024
c7f8d53
update: OOP inheritance
VsevolodX Aug 1, 2024
3861d28
update: rename slab -> material
VsevolodX Aug 1, 2024
83325e3
update: rename deformation -> perturbation
VsevolodX Aug 1, 2024
e512d29
update: make work with distinct builders
VsevolodX Aug 1, 2024
678eda1
update: use OOP and cleanup
VsevolodX Aug 1, 2024
a879f52
update: small adjustments
VsevolodX Aug 1, 2024
7cb6053
chore: add docstring
VsevolodX Aug 1, 2024
07cf54c
update: even more optimization
VsevolodX Aug 1, 2024
2b0805a
update: fix a mistake
VsevolodX Aug 2, 2024
7d410a7
feat: add cell matching dpspb
VsevolodX Aug 2, 2024
2ecd3cf
feat: add method to create lattice
VsevolodX Aug 2, 2024
34eec9e
update: adjustments:
VsevolodX Aug 2, 2024
e8f403a
update: add a test for perturbations
VsevolodX Aug 2, 2024
4d44046
update: small but nessessary change
VsevolodX Aug 2, 2024
830140a
update: noticed and issue with name and metadata
VsevolodX Aug 2, 2024
9e3642c
chore: fix lint
VsevolodX Aug 2, 2024
49e148b
chore: types fixes
VsevolodX Aug 2, 2024
aa757fc
update: add flag for ease of use
VsevolodX Aug 3, 2024
460a321
update: add a constructor from arrays
VsevolodX Aug 5, 2024
1eea222
update: move coordinate related utils to a folder
VsevolodX Aug 5, 2024
ee92a58
update: rename nested_array -> vectors_array
VsevolodX Aug 5, 2024
9d4c882
update: move defs from defs
VsevolodX Aug 6, 2024
427994e
update: minor fix
VsevolodX Aug 6, 2024
bf561bb
chore: run lint fix
VsevolodX Aug 6, 2024
bccdeb1
update: move sine realated and add factory
VsevolodX Aug 6, 2024
88d95cd
update: cleanup
VsevolodX Aug 6, 2024
b34ae4c
update: use factory for pertrubation
VsevolodX Aug 6, 2024
37db051
chore: run lint fix
VsevolodX Aug 6, 2024
3806063
update: add test for distance preserving perturbation
VsevolodX Aug 6, 2024
ccf9702
update: add json as method:
VsevolodX Aug 7, 2024
ae3cdcb
chore: add types
VsevolodX Aug 7, 2024
5fca5dc
chore: remove duplicated method
VsevolodX Aug 7, 2024
e8a22a1
update: change folders and import
VsevolodX Aug 7, 2024
98a4358
update: renames
VsevolodX Aug 7, 2024
3697be4
chore: run lint fix
VsevolodX Aug 7, 2024
a42ebbd
update: optimize ann OOP
VsevolodX Aug 7, 2024
e8f4947
chore: add a todo memo
VsevolodX Aug 7, 2024
f84e873
update: optimize with OOP
VsevolodX Aug 7, 2024
963822a
update: add set coordintes as a method
VsevolodX Aug 7, 2024
5f2dcf4
update: rearrange
VsevolodX Aug 11, 2024
8ce0856
update: remove tuples
VsevolodX Aug 12, 2024
4c6a391
update: rename
VsevolodX Aug 12, 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
4 changes: 2 additions & 2 deletions src/py/mat3ra/made/basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ def from_dict(
coordinates: List[Dict],
units: str,
labels: Optional[List[Dict]] = None,
cell: Optional[Dict] = None,
cell: Optional[List[List[float]]] = None,
constraints: Optional[List[Dict]] = None,
) -> "Basis":
return Basis(
elements=ArrayWithIds.from_list_of_dicts(elements),
coordinates=ArrayWithIds.from_list_of_dicts(coordinates),
units=units,
cell=Cell.from_nested_array(cell),
cell=Cell.from_vectors_array(cell),
labels=ArrayWithIds.from_list_of_dicts(labels) if labels else ArrayWithIds(values=[]),
constraints=ArrayWithIds.from_list_of_dicts(constraints) if constraints else ArrayWithIds(values=[]),
)
Expand Down
32 changes: 16 additions & 16 deletions src/py/mat3ra/made/cell.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List
from typing import List, Optional

import numpy as np
from mat3ra.utils.mixins import RoundNumericValuesMixin
Expand All @@ -13,13 +13,13 @@ class Cell(RoundNumericValuesMixin, BaseModel):
__round_precision__ = 6

@classmethod
def from_nested_array(cls, nested_array):
if nested_array is None:
nested_array = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
return cls(vector1=nested_array[0], vector2=nested_array[1], vector3=nested_array[2])
def from_vectors_array(cls, vectors_array: Optional[List[List[float]]] = None) -> "Cell":
if vectors_array is None:
vectors_array = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
return cls(vector1=vectors_array[0], vector2=vectors_array[1], vector3=vectors_array[2])

@property
def vectors_as_nested_array(self, skip_rounding=False) -> List[List[float]]:
def vectors_as_array(self, skip_rounding=False) -> List[List[float]]:
if skip_rounding:
return [self.vector1, self.vector2, self.vector3]
return self.round_array_or_number([self.vector1, self.vector2, self.vector3])
Expand All @@ -32,22 +32,22 @@ def to_json(self, skip_rounding=False):
self.vector3 if skip_rounding else _(self.vector3),
]

def clone(self):
return self.from_nested_array(self.vectors_as_nested_array)
def clone(self) -> "Cell":
return self.from_vectors_array(self.vectors_as_array)

def clone_and_scale_by_matrix(self, matrix):
def clone_and_scale_by_matrix(self, matrix: List[List[float]]) -> "Cell":
new_cell = self.clone()
new_cell.scale_by_matrix(matrix)
return new_cell

def convert_point_to_cartesian(self, point):
np_vector = np.array(self.vectors_as_nested_array)
def convert_point_to_cartesian(self, point: List[float]) -> List[float]:
np_vector = np.array(self.vectors_as_array)
return np.dot(point, np_vector)

def convert_point_to_crystal(self, point):
np_vector = np.array(self.vectors_as_nested_array)
def convert_point_to_crystal(self, point: List[float]) -> List[float]:
np_vector = np.array(self.vectors_as_array)
return np.dot(point, np.linalg.inv(np_vector))

def scale_by_matrix(self, matrix):
np_vector = np.array(self.vectors_as_nested_array)
self.vector1, self.vector2, self.vector3 = np.dot(matrix, np_vector).tolist()
def scale_by_matrix(self, matrix: List[List[float]]):
np_vector = np.array(self.vectors_as_array)
self.vector1, self.vector2, self.vector3 = np.dot(np.array(matrix), np_vector).tolist()
34 changes: 28 additions & 6 deletions src/py/mat3ra/made/lattice.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from .cell import Cell

HASH_TOLERANCE = 3
DEFAULT_UNITS = {"length": "angstrom", "angle": "degree"}
DEFAULT_TYPE = "TRI"


class Lattice(RoundNumericValuesMixin, BaseModel):
Expand All @@ -17,11 +19,8 @@ class Lattice(RoundNumericValuesMixin, BaseModel):
alpha: float = 90.0
beta: float = 90.0
gamma: float = 90.0
units: Dict[str, str] = {
"length": "angstrom",
"angle": "degree",
}
type: str = "TRI"
units: Dict[str, str] = DEFAULT_UNITS
type: str = DEFAULT_TYPE

@property
def vectors(self) -> List[List[float]]:
Expand Down Expand Up @@ -52,6 +51,29 @@ def vectors(self) -> List[List[float]]:
[0.0, 0.0, c],
]

@classmethod
def from_vectors_array(
cls, vectors: List[List[float]], units: Optional[Dict[str, str]] = None, type: Optional[str] = None
) -> "Lattice":
"""
Create a Lattice object from a nested array of vectors.
Args:
vectors (List[List[float]]): A nested array of vectors.
Returns:
Lattice: A Lattice object.
"""
a = np.linalg.norm(vectors[0])
b = np.linalg.norm(vectors[1])
c = np.linalg.norm(vectors[2])
alpha = np.degrees(np.arccos(np.dot(vectors[1], vectors[2]) / (b * c)))
beta = np.degrees(np.arccos(np.dot(vectors[0], vectors[2]) / (a * c)))
gamma = np.degrees(np.arccos(np.dot(vectors[0], vectors[1]) / (a * b)))
if units is None:
units = DEFAULT_UNITS
if type is None:
type = DEFAULT_TYPE
return cls(a=float(a), b=float(b), c=float(c), alpha=alpha, beta=beta, gamma=gamma, units=units, type=type)

def to_json(self, skip_rounding: bool = False) -> Dict[str, Any]:
__round__ = RoundNumericValuesMixin.round_array_or_number
round_func = __round__ if not skip_rounding else lambda x: x
Expand All @@ -78,7 +100,7 @@ def vector_arrays(self) -> List[List[float]]:

@property
def cell(self) -> Cell:
return Cell.from_nested_array(self.vector_arrays)
return Cell.from_vectors_array(self.vector_arrays)

def volume(self) -> float:
np_vector = np.array(self.vector_arrays)
Expand Down
5 changes: 5 additions & 0 deletions src/py/mat3ra/made/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,8 @@ def to_crystal(self) -> None:
new_basis = self.basis.copy()
new_basis.to_crystal()
self.basis = new_basis

def set_coordinates(self, coordinates: List[List[float]]) -> None:
new_basis = self.basis.copy()
new_basis.coordinates.values = coordinates
self.basis = new_basis
13 changes: 7 additions & 6 deletions src/py/mat3ra/made/tools/build/defect/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
get_closest_site_id_from_coordinate_and_element,
)
from ....utils import get_center_of_coordinates
from ...utils import transform_coordinate_to_supercell, CoordinateConditionBuilder
from ...utils import transform_coordinate_to_supercell
from ...utils import coordinate as CoordinateCondition
from ..utils import merge_materials
from ..slab import SlabConfiguration, create_slab, Termination
from ..supercell import create_supercell
Expand Down Expand Up @@ -436,7 +437,7 @@ def condition(coordinate: List[float]):
return self.merge_slab_and_defect(island_material, new_material)

def _generate(self, configuration: _ConfigurationType) -> List[_GeneratedItemType]:
condition_callable, _ = configuration.condition
condition_callable = configuration.condition.condition
return [
self.create_island(
material=configuration.crystal,
Expand All @@ -463,7 +464,7 @@ def _calculate_cut_direction_vector(self, material: Material, cut_direction: Lis
The normalized cut direction vector in Cartesian coordinates.
"""
np_cut_direction = np.array(cut_direction)
direction_vector = np.dot(np.array(material.basis.cell.vectors_as_nested_array), np_cut_direction)
direction_vector = np.dot(np.array(material.basis.cell.vectors_as_array), np_cut_direction)
normalized_direction_vector = direction_vector / np.linalg.norm(direction_vector)
return normalized_direction_vector

Expand Down Expand Up @@ -499,7 +500,7 @@ def _calculate_rotation_parameters(
"""
height_cartesian = self._calculate_height_cartesian(original_material, new_material)
cut_direction_xy_proj_cart = np.linalg.norm(
np.dot(np.array(new_material.basis.cell.vectors_as_nested_array), normalized_direction_vector)
np.dot(np.array(new_material.basis.cell.vectors_as_array), normalized_direction_vector)
)
# Slope of the terrace along the cut direction
hypotenuse = np.linalg.norm([height_cartesian, cut_direction_xy_proj_cart])
Expand Down Expand Up @@ -592,10 +593,10 @@ def create_terrace(
)

normalized_direction_vector = self._calculate_cut_direction_vector(material, cut_direction)
condition, _ = CoordinateConditionBuilder.plane(
condition = CoordinateCondition.PlaneCoordinateCondition(
plane_normal=normalized_direction_vector,
plane_point_coordinate=pivot_coordinate,
)
).condition
atoms_within_terrace = filter_by_condition_on_coordinates(
material=material_with_additional_layers,
condition=condition,
Expand Down
27 changes: 20 additions & 7 deletions src/py/mat3ra/made/tools/build/defect/configuration.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
from typing import Optional, List, Any, Callable, Dict, Tuple, Union
from typing import Optional, List, Union
from pydantic import BaseModel

from mat3ra.code.entity import InMemoryEntity
from mat3ra.made.material import Material

from ...analyze import get_closest_site_id_from_coordinate, get_atomic_coordinates_extremum
from ...utils import CoordinateConditionBuilder
from ...utils.coordinate import (
CylinderCoordinateCondition,
SphereCoordinateCondition,
BoxCoordinateCondition,
TriangularPrismCoordinateCondition,
PlaneCoordinateCondition,
)
from .enums import PointDefectTypeEnum, SlabDefectTypeEnum, AtomPlacementMethodEnum, ComplexDefectTypeEnum


class BaseDefectConfiguration(BaseModel):
# TODO: fix arbitrary_types_allowed error and set Material class type
crystal: Any = None
crystal: Material = None

class Config:
arbitrary_types_allowed = True

@property
def _json(self):
Expand Down Expand Up @@ -169,16 +177,21 @@ class IslandSlabDefectConfiguration(SlabDefectConfiguration):
"""

defect_type: SlabDefectTypeEnum = SlabDefectTypeEnum.ISLAND
condition: Optional[Tuple[Callable[[List[float]], bool], Dict]] = CoordinateConditionBuilder().cylinder()
condition: Union[
CylinderCoordinateCondition,
SphereCoordinateCondition,
BoxCoordinateCondition,
TriangularPrismCoordinateCondition,
PlaneCoordinateCondition,
] = CylinderCoordinateCondition()

@property
def _json(self):
_, condition_json = self.condition
return {
**super()._json,
"type": self.get_cls_name(),
"defect_type": self.defect_type.name,
"condition": condition_json,
"condition": self.condition.get_json(),
}


Expand Down
9 changes: 7 additions & 2 deletions src/py/mat3ra/made/tools/build/interface/builders.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Any, List, Optional

import numpy as np
from mat3ra.made.tools.modify import translate_to_z_level
from pydantic import BaseModel
from ase.build.tools import niggli_reduce
from pymatgen.analysis.interfaces.coherent_interfaces import (
Expand Down Expand Up @@ -144,9 +145,13 @@ class ZSLStrainMatchingInterfaceBuilder(ConvertGeneratedItemsPymatgenStructureMi

def _generate(self, configuration: InterfaceConfiguration) -> List[PymatgenInterface]:
generator = ZSLGenerator(**self.build_parameters.strain_matching_parameters.dict())
substrate_with_atoms_translated_to_bottom = translate_to_z_level(
configuration.substrate_configuration.bulk, "bottom"
)
film_with_atoms_translated_to_bottom = translate_to_z_level(configuration.film_configuration.bulk, "bottom")
builder = CoherentInterfaceBuilder(
substrate_structure=to_pymatgen(configuration.substrate_configuration.bulk),
film_structure=to_pymatgen(configuration.film_configuration.bulk),
substrate_structure=to_pymatgen(substrate_with_atoms_translated_to_bottom),
film_structure=to_pymatgen(film_with_atoms_translated_to_bottom),
substrate_miller=configuration.substrate_configuration.miller_indices,
film_miller=configuration.film_configuration.miller_indices,
zslgen=generator,
Expand Down
37 changes: 37 additions & 0 deletions src/py/mat3ra/made/tools/build/perturbation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from typing import Union, Optional

from mat3ra.made.material import Material
from .builders import (
SlabPerturbationBuilder,
DistancePreservingSlabPerturbationBuilder,
CellMatchingDistancePreservingSlabPerturbationBuilder,
)
from .configuration import PerturbationConfiguration


def create_perturbation(
configuration: PerturbationConfiguration,
preserve_distance: Optional[bool] = False,
builder: Union[
SlabPerturbationBuilder,
DistancePreservingSlabPerturbationBuilder,
CellMatchingDistancePreservingSlabPerturbationBuilder,
None,
] = None,
) -> Material:
"""
Return a material with a perturbation applied.

Args:
configuration: The configuration of the perturbation to be applied.
preserve_distance: If True, the builder that preserves the distance between atoms is used.
builder: The builder to be used to create the perturbation.

Returns:
The material with the perturbation applied.
"""
if builder is None:
builder = SlabPerturbationBuilder()
if preserve_distance:
builder = CellMatchingDistancePreservingSlabPerturbationBuilder()
return builder.get_material(configuration)
Loading
Loading