-
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 #158 from Exabyte-io/feature/SOF-7426
feature/SOF-7426 feat: displace film minimizing norm of distances
- Loading branch information
Showing
11 changed files
with
313 additions
and
58 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
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
11 changes: 6 additions & 5 deletions
11
src/py/mat3ra/made/tools/calculate.py → ...y/mat3ra/made/tools/calculate/__init__.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
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,122 @@ | ||
from typing import Callable | ||
|
||
import numpy as np | ||
from mat3ra.made.material import Material | ||
from pydantic import BaseModel | ||
|
||
from ..analyze import get_surface_atom_indices | ||
from ..convert.utils import InterfacePartsEnum | ||
from ..enums import SurfaceTypes | ||
from ..modify import get_interface_part | ||
from .interaction_functions import sum_of_inverse_distances_squared | ||
|
||
|
||
class MaterialCalculatorParameters(BaseModel): | ||
""" | ||
Defines the parameters for a material calculator. | ||
Args: | ||
interaction_function (Callable): A function used to calculate the interaction metric between | ||
sets of coordinates. The default function is sum_of_inverse_distances_squared. | ||
""" | ||
|
||
interaction_function: Callable = sum_of_inverse_distances_squared | ||
|
||
|
||
class InterfaceMaterialCalculatorParameters(MaterialCalculatorParameters): | ||
""" | ||
Parameters specific to the calculation of interaction energies between | ||
an interface material's film and substrate. | ||
Args: | ||
shadowing_radius (float): Radius used to determine the surface atoms of the film or substrate | ||
for interaction calculations. Default is 2.5 Å. | ||
""" | ||
|
||
shadowing_radius: float = 2.5 | ||
|
||
|
||
class MaterialCalculator(BaseModel): | ||
""" | ||
A base class for performing calculations on materials. | ||
This class uses the parameters defined in MaterialCalculatorParameters to calculate | ||
interaction metrics between atoms or sets of coordinates within the material. | ||
Args: | ||
calculator_parameters (MaterialCalculatorParameters): Parameters controlling the calculator, | ||
including the interaction function. | ||
""" | ||
|
||
calculator_parameters: MaterialCalculatorParameters = MaterialCalculatorParameters() | ||
|
||
def get_energy(self, material: Material): | ||
""" | ||
Calculate the energy (or other metric) for a material. | ||
Args: | ||
material (Material): The material to calculate the interaction energy for. | ||
Returns: | ||
float: The interaction energy between the coordinates of the material, | ||
calculated using the specified interaction function. | ||
""" | ||
return self.calculator_parameters.interaction_function(material.coordinates, material.coordinates) | ||
|
||
|
||
class InterfaceMaterialCalculator(MaterialCalculator): | ||
""" | ||
A specialized calculator for computing the interaction energy between a film and a substrate | ||
in an interface material. | ||
This class extends MaterialCalculator and uses additional parameters specific to interface materials, | ||
such as the shadowing radius to detect surface atoms. | ||
Args: | ||
calculator_parameters (InterfaceMaterialCalculatorParameters): Parameters that include the | ||
shadowing radius and interaction function. | ||
""" | ||
|
||
calculator_parameters: InterfaceMaterialCalculatorParameters = InterfaceMaterialCalculatorParameters() | ||
|
||
def get_energy( | ||
self, | ||
material: Material, | ||
shadowing_radius: float = 2.5, | ||
interaction_function: Callable = sum_of_inverse_distances_squared, | ||
) -> float: | ||
""" | ||
Calculate the interaction energy between the film and substrate in an interface material. | ||
This method uses the shadowing radius to detect surface atoms and applies the given | ||
interaction function to calculate the interaction between the film and substrate. | ||
Args: | ||
material (Material): The interface Material object consisting of both the film and substrate. | ||
shadowing_radius (float): The radius used to detect surface atoms for the interaction | ||
calculation. Defaults to 2.5 Å. | ||
interaction_function (Callable): A function to compute the interaction between the film and | ||
substrate. Defaults to sum_of_inverse_distances_squared. | ||
Returns: | ||
float: The calculated interaction energy between the film and substrate. | ||
""" | ||
film_material = get_interface_part(material, part=InterfacePartsEnum.FILM) | ||
substrate_material = get_interface_part(material, part=InterfacePartsEnum.SUBSTRATE) | ||
|
||
film_surface_atom_indices = get_surface_atom_indices( | ||
film_material, SurfaceTypes.BOTTOM, shadowing_radius=shadowing_radius | ||
) | ||
substrate_surface_atom_indices = get_surface_atom_indices( | ||
substrate_material, SurfaceTypes.TOP, shadowing_radius=shadowing_radius | ||
) | ||
|
||
film_surface_atom_coordinates = film_material.basis.coordinates | ||
film_surface_atom_coordinates.filter_by_ids(film_surface_atom_indices) | ||
substrate_surface_atom_coordinates = substrate_material.basis.coordinates | ||
substrate_surface_atom_coordinates.filter_by_ids(substrate_surface_atom_indices) | ||
|
||
film_coordinates_values = np.array(film_surface_atom_coordinates.values) | ||
substrate_coordinates_values = np.array(substrate_surface_atom_coordinates.values) | ||
|
||
return interaction_function(film_coordinates_values, substrate_coordinates_values) |
23 changes: 23 additions & 0 deletions
23
src/py/mat3ra/made/tools/calculate/interaction_functions.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,23 @@ | ||
import numpy as np | ||
|
||
|
||
def sum_of_inverse_distances_squared( | ||
coordinates_1: np.ndarray, coordinates_2: np.ndarray, epsilon: float = 1e-12 | ||
) -> float: | ||
""" | ||
Calculate the sum of inverse squares of distances between two sets of coordinates. | ||
Args: | ||
coordinates_1 (np.ndarray): The first set of coordinates, shape (N1, 3). | ||
coordinates_2 (np.ndarray): The second set of coordinates, shape (N2, 3). | ||
epsilon (float): Small value to prevent division by zero. | ||
Returns: | ||
float: The calculated sum. | ||
""" | ||
differences = coordinates_1[:, np.newaxis, :] - coordinates_2[np.newaxis, :, :] # Shape: (N1, N2, 3) | ||
distances_squared = np.sum(differences**2, axis=2) # Shape: (N1, N2) | ||
distances_squared = np.where(distances_squared == 0, epsilon, distances_squared) | ||
inv_distances_squared = -1 / distances_squared | ||
total = np.sum(inv_distances_squared) | ||
return float(total) |
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,55 @@ | ||
from typing import Any, Callable, Dict, List, Optional, Tuple | ||
|
||
import numpy as np | ||
from mat3ra.made.material import Material | ||
|
||
|
||
def evaluate_calculator_on_xy_grid( | ||
material: Material, | ||
calculator_function: Callable[[Material], Any], | ||
modifier: Optional[Callable] = None, | ||
modifier_parameters: Dict[str, Any] = {}, | ||
grid_size_xy: Tuple[int, int] = (10, 10), | ||
grid_offset_position: List[float] = [0, 0], | ||
grid_range_x: Tuple[float, float] = (-0.5, 0.5), | ||
grid_range_y: Tuple[float, float] = (-0.5, 0.5), | ||
use_cartesian_coordinates: bool = False, | ||
) -> Tuple[List[np.ndarray], np.ndarray]: | ||
""" | ||
Calculate a property on a grid of x-y positions. | ||
Args: | ||
material (Material): The material object. | ||
modifier (Callable): The modifier function to apply to the material. | ||
modifier_parameters (Dict[str, Any]): The parameters to pass to the modifier. | ||
calculator_function (Callable): The calculator function to apply to the modified material. | ||
grid_size_xy (Tuple[int, int]): The size of the grid in x and y directions. | ||
grid_offset_position (List[float]): The offset position of the grid, in Angstroms or crystal coordinates. | ||
grid_range_x (Tuple[float, float]): The range to search in x direction, in Angstroms or crystal coordinates. | ||
grid_range_y (Tuple[float, float]): The range to search in y direction, in Angstroms or crystal coordinates. | ||
use_cartesian_coordinates (bool): Whether to use Cartesian coordinates. | ||
Returns: | ||
Tuple[List[np.ndarray[float]], np.ndarray[float]]: The x-y positions and the calculated property values. | ||
""" | ||
x_values = np.linspace(grid_range_x[0], grid_range_x[1], grid_size_xy[0]) + grid_offset_position[0] | ||
y_values = np.linspace(grid_range_y[0], grid_range_y[1], grid_size_xy[1]) + grid_offset_position[1] | ||
|
||
xy_matrix = [x_values, y_values] | ||
results_matrix = np.zeros(grid_size_xy) | ||
|
||
for i, x in enumerate(x_values): | ||
for j, y in enumerate(y_values): | ||
if modifier is None: | ||
modified_material = material | ||
else: | ||
modified_material = modifier( | ||
material, | ||
displacement=[x, y, 0], | ||
use_cartesian_coordinates=use_cartesian_coordinates, | ||
**modifier_parameters, | ||
) | ||
result = calculator_function(modified_material) | ||
results_matrix[i, j] = result | ||
|
||
return xy_matrix, results_matrix |
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
Oops, something went wrong.