@mat3ra/made 2026.7.29-0 → 2026.8.7-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -17,6 +17,47 @@ def translate(material: Material, vector: Vector3D) -> Material:
|
|
|
17
17
|
return translate_by_vector(material, vector, use_cartesian_coordinates=True)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def translate_atoms(
|
|
21
|
+
material: Material,
|
|
22
|
+
atom_ids: List[int],
|
|
23
|
+
vector: Vector3D,
|
|
24
|
+
use_cartesian_coordinates: bool = True,
|
|
25
|
+
) -> Material:
|
|
26
|
+
"""
|
|
27
|
+
Translate the given atoms by a vector.
|
|
28
|
+
|
|
29
|
+
Basis order is preserved.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
material (Material): The material to translate atoms in.
|
|
33
|
+
atom_ids (List[int]): Indices of the atoms to translate.
|
|
34
|
+
vector (Vector3D): The vector to translate the atoms by.
|
|
35
|
+
use_cartesian_coordinates (bool): Whether `vector` is in cartesian coordinates.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Material: The material with the given atoms translated.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
IndexError: If any atom id is out of range for the basis.
|
|
42
|
+
"""
|
|
43
|
+
new_material = material.clone()
|
|
44
|
+
if use_cartesian_coordinates:
|
|
45
|
+
new_material.to_cartesian()
|
|
46
|
+
coordinates: List[List[float]] = [list(coordinate) for coordinate in new_material.basis.coordinates.values]
|
|
47
|
+
|
|
48
|
+
out_of_range = [atom_id for atom_id in atom_ids if not -len(coordinates) <= atom_id < len(coordinates)]
|
|
49
|
+
if out_of_range:
|
|
50
|
+
raise IndexError(f"Atom ids {out_of_range} are out of range for a basis of {len(coordinates)} atoms.")
|
|
51
|
+
|
|
52
|
+
for atom_id in atom_ids:
|
|
53
|
+
coordinates[atom_id] = (np.array(coordinates[atom_id]) + np.array(vector)).tolist()
|
|
54
|
+
|
|
55
|
+
new_material.set_coordinates(coordinates)
|
|
56
|
+
if use_cartesian_coordinates:
|
|
57
|
+
new_material.to_crystal()
|
|
58
|
+
return new_material
|
|
59
|
+
|
|
60
|
+
|
|
20
61
|
@decorator_convert_supercell_matrix_2x2_to_3x3
|
|
21
62
|
def supercell(material: MaterialWithBuildMetadata, supercell_matrix) -> MaterialWithBuildMetadata:
|
|
22
63
|
atoms = to_ase(material)
|
|
@@ -5,9 +5,11 @@ import pytest
|
|
|
5
5
|
from mat3ra.esse.models.core.abstract.matrix_3x3 import Matrix3x3Schema
|
|
6
6
|
from mat3ra.esse.models.core.reusable.axis_enum import AxisEnum
|
|
7
7
|
from mat3ra.made.material import Material
|
|
8
|
-
from mat3ra.made.tools.build_components.operations.core.modifications.perturb import
|
|
8
|
+
from mat3ra.made.tools.build_components.operations.core.modifications.perturb import (
|
|
9
|
+
SineWavePerturbationFunctionHolder,
|
|
10
|
+
)
|
|
9
11
|
from mat3ra.made.tools.operations.core.binary import stack_two_materials
|
|
10
|
-
from mat3ra.made.tools.operations.core.unary import perturb, strain
|
|
12
|
+
from mat3ra.made.tools.operations.core.unary import perturb, strain, translate_atoms
|
|
11
13
|
from mat3ra.made.tools.operations.reusable.unary import transform_material_by_matrix
|
|
12
14
|
from unit.fixtures.bulk import BULK_Si_CONVENTIONAL
|
|
13
15
|
from unit.fixtures.strain import BULK_Si_CONVENTIONAL_STRAINED
|
|
@@ -100,3 +102,25 @@ def test_transform_material_by_matrix(matrix, expected_a, expected_b, expected_c
|
|
|
100
102
|
assert transformed_material.lattice.a == pytest.approx(expected_a, abs=1e-6)
|
|
101
103
|
assert transformed_material.lattice.b == pytest.approx(expected_b, abs=1e-6)
|
|
102
104
|
assert transformed_material.lattice.c == pytest.approx(expected_c, abs=1e-6)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@pytest.mark.parametrize(
|
|
108
|
+
"material_config, atom_ids, vector, expected_coord_change",
|
|
109
|
+
[
|
|
110
|
+
(SI_CONVENTIONAL_SLAB_001, [3], [0.0, 0.0, -1.5], [0.0, 0.0, -1.5]),
|
|
111
|
+
],
|
|
112
|
+
)
|
|
113
|
+
def test_translate_atoms(material_config, atom_ids, vector, expected_coord_change):
|
|
114
|
+
material = Material.create(material_config)
|
|
115
|
+
original_coords = [coord[:] for coord in material.basis.coordinates.values]
|
|
116
|
+
|
|
117
|
+
translated_material = translate_atoms(material, atom_ids, vector)
|
|
118
|
+
lattice_vectors = np.array(material.lattice.vector_arrays)
|
|
119
|
+
|
|
120
|
+
actual_delta = (
|
|
121
|
+
np.array(translated_material.basis.coordinates.values[atom_ids[0]]) - np.array(original_coords[atom_ids[0]])
|
|
122
|
+
) @ lattice_vectors
|
|
123
|
+
assert np.isclose(actual_delta, expected_coord_change, atol=1e-4).all()
|
|
124
|
+
|
|
125
|
+
untouched_id = atom_ids[0] - 1
|
|
126
|
+
assert np.allclose(translated_material.basis.coordinates.values[untouched_id], original_coords[untouched_id])
|