@mat3ra/made 2026.4.1-0 → 2026.4.2-1
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 +1 -1
- package/src/py/mat3ra/made/lattice.py +14 -0
- package/src/py/mat3ra/made/tools/build_components/operations/core/modifications/strain/builder.py +8 -0
- package/src/py/mat3ra/made/tools/build_components/operations/core/modifications/strain/configuration.py +11 -1
- package/src/py/mat3ra/made/tools/build_components/operations/core/modifications/strain/helpers.py +22 -3
- package/tests/py/unit/test_lattice.py +25 -0
- package/tests/py/unit/test_tools_build_strain.py +10 -0
package/package.json
CHANGED
|
@@ -128,6 +128,20 @@ class Lattice(RoundNumericValuesMixin, LatticeSchemaVectorless, InMemoryEntityPy
|
|
|
128
128
|
def cell_volume_rounded(self) -> float:
|
|
129
129
|
return self.vectors.volume_rounded
|
|
130
130
|
|
|
131
|
+
@property
|
|
132
|
+
def reciprocal_vectors(self):
|
|
133
|
+
return np.linalg.inv(np.array(self.vector_arrays, dtype=float)).T.tolist()
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def reciprocal_vector_norms(self) -> List[float]:
|
|
137
|
+
return [float(np.linalg.norm(vector)) for vector in self.reciprocal_vectors]
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def reciprocal_vector_ratios(self) -> List[float]:
|
|
141
|
+
norms = self.reciprocal_vector_norms
|
|
142
|
+
max_norm = max(norms)
|
|
143
|
+
return [round(float(value / max_norm), 3) for value in norms]
|
|
144
|
+
|
|
131
145
|
def get_hash_string(self, is_scaled: bool = False) -> str:
|
|
132
146
|
"""Mirrors JS Lattice.getHashString(isScaled). Rounds to HASH_TOLERANCE decimal places."""
|
|
133
147
|
scale = self.a if is_scaled else 1
|
package/src/py/mat3ra/made/tools/build_components/operations/core/modifications/strain/builder.py
CHANGED
|
@@ -15,3 +15,11 @@ class StrainBuilder(BaseSingleBuilder):
|
|
|
15
15
|
def _generate(self, configuration: StrainConfiguration) -> MaterialWithBuildMetadata:
|
|
16
16
|
strained_material = strain(configuration.material, configuration.strain_matrix)
|
|
17
17
|
return MaterialWithBuildMetadata.create(strained_material.to_dict())
|
|
18
|
+
|
|
19
|
+
def _update_material_name(
|
|
20
|
+
self, material: MaterialWithBuildMetadata, configuration: StrainConfiguration
|
|
21
|
+
) -> MaterialWithBuildMetadata:
|
|
22
|
+
if configuration.scale_factor is not None:
|
|
23
|
+
base_name = configuration.material.name
|
|
24
|
+
material.name = f"{base_name} (scale={configuration.scale_factor:.4f})"
|
|
25
|
+
return material
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Union
|
|
1
|
+
from typing import Optional, Union
|
|
2
2
|
|
|
3
3
|
from mat3ra.esse.models.core.abstract.matrix_3x3 import Matrix3x3Schema
|
|
4
4
|
from mat3ra.made.material import Material
|
|
@@ -8,6 +8,16 @@ from ..... import MaterialWithBuildMetadata
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class StrainConfiguration(BaseConfigurationPydantic):
|
|
11
|
+
"""
|
|
12
|
+
Configuration for a strain operation on a Material.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
material (Material): The Material object to be strained.
|
|
16
|
+
strain_matrix (Matrix3x3Schema): The 3x3 strain matrix defining the deformation.
|
|
17
|
+
scale_factor (Optional[float]): An optional scale factor for isotropic scaling instead of a full strain matrix.
|
|
18
|
+
"""
|
|
19
|
+
|
|
11
20
|
type: str = "StrainConfiguration"
|
|
12
21
|
material: Union[Material, MaterialWithBuildMetadata]
|
|
13
22
|
strain_matrix: Matrix3x3Schema
|
|
23
|
+
scale_factor: Optional[float] = None
|
package/src/py/mat3ra/made/tools/build_components/operations/core/modifications/strain/helpers.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Union
|
|
1
|
+
from typing import Optional, Union
|
|
2
2
|
|
|
3
3
|
from mat3ra.esse.models.core.abstract.matrix_3x3 import Matrix3x3Schema
|
|
4
4
|
from mat3ra.made.material import Material
|
|
@@ -23,8 +23,27 @@ def get_isotropic_strain_matrix(scale_factor: float) -> Matrix3x3Schema:
|
|
|
23
23
|
|
|
24
24
|
def create_strain(
|
|
25
25
|
material: Union[Material, MaterialWithBuildMetadata],
|
|
26
|
-
strain_matrix: Matrix3x3Schema,
|
|
26
|
+
strain_matrix: Optional[Matrix3x3Schema] = None,
|
|
27
|
+
scale_factor: Optional[float] = None,
|
|
27
28
|
) -> MaterialWithBuildMetadata:
|
|
28
|
-
|
|
29
|
+
"""
|
|
30
|
+
Creates a strained Material based on the provided strain matrix or scale factor.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
material (Union[Material, MaterialWithBuildMetadata]): The material to be strained.
|
|
34
|
+
strain_matrix (Optional[Matrix3x3Schema]): The 3x3 strain matrix defining the deformation.
|
|
35
|
+
scale_factor (Optional[float]): An optional scale factor for isotropic scaling instead of a full strain matrix.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
MaterialWithBuildMetadata: The strained material with build metadata.
|
|
39
|
+
"""
|
|
40
|
+
if scale_factor is not None:
|
|
41
|
+
if scale_factor <= 0:
|
|
42
|
+
raise ValueError("scale_factor must be positive.")
|
|
43
|
+
strain_matrix = get_isotropic_strain_matrix(scale_factor)
|
|
44
|
+
if strain_matrix is None:
|
|
45
|
+
raise ValueError("Either strain_matrix or scale_factor must be provided.")
|
|
46
|
+
|
|
47
|
+
configuration = StrainConfiguration(material=material, strain_matrix=strain_matrix, scale_factor=scale_factor)
|
|
29
48
|
builder = StrainBuilder()
|
|
30
49
|
return builder.get_material(configuration)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
1
3
|
from mat3ra.code.vector import RoundedVector3D
|
|
2
4
|
from mat3ra.made.lattice import Lattice
|
|
3
5
|
from mat3ra.utils import assertion as assertion_utils
|
|
@@ -75,5 +77,28 @@ def test_lattice_get_scaled_by_matrix():
|
|
|
75
77
|
assertion_utils.assert_deep_almost_equal(lattice.vector_arrays, expected_vector_values)
|
|
76
78
|
|
|
77
79
|
|
|
80
|
+
def test_reciprocal_vectors():
|
|
81
|
+
lattice = Lattice(a=2.0, b=3.0, c=4.0)
|
|
82
|
+
expected_vectors = [[0.5, 0.0, 0.0], [0.0, 1 / 3, 0.0], [0.0, 0.0, 0.25]]
|
|
83
|
+
assertion_utils.assert_deep_almost_equal(lattice.reciprocal_vectors, expected_vectors)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_reciprocal_vector_norms():
|
|
87
|
+
lattice = Lattice(a=2.0, b=3.0, c=4.0)
|
|
88
|
+
expected_norms = [0.5, 1 / 3, 0.25]
|
|
89
|
+
assertion_utils.assert_deep_almost_equal(lattice.reciprocal_vector_norms, expected_norms)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@pytest.mark.parametrize(
|
|
93
|
+
"lattice, expected",
|
|
94
|
+
[
|
|
95
|
+
(Lattice(a=2.0, b=3.0, c=4.0), [1.0, 0.667, 0.5]),
|
|
96
|
+
(Lattice(a=5.43, b=5.43, c=5.43), [1.0, 1.0, 1.0]),
|
|
97
|
+
],
|
|
98
|
+
)
|
|
99
|
+
def test_reciprocal_vector_ratios(lattice, expected):
|
|
100
|
+
assert lattice.reciprocal_vector_ratios == expected
|
|
101
|
+
|
|
102
|
+
|
|
78
103
|
# to test: create, calculate_vectors, from_vectors, get_lattice_type, clone
|
|
79
104
|
# from_vectors, to_dict, cell, cell_volume, scale_by_matrix, update_from_lattice,
|
|
@@ -24,3 +24,13 @@ def test_create_strain(material_config, strain_matrix, expected_material_config)
|
|
|
24
24
|
assert build_step.configuration["type"] == "StrainConfiguration"
|
|
25
25
|
assert build_step.configuration["strain_matrix"] == strain_matrix
|
|
26
26
|
assert build_step.build_parameters == {}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_create_strain_with_scale_factor():
|
|
30
|
+
material = Material.create(BULK_Si_CONVENTIONAL)
|
|
31
|
+
strained_material = create_strain(material, scale_factor=1.1)
|
|
32
|
+
|
|
33
|
+
assert strained_material.name == f"{material.name} (scale=1.1000)"
|
|
34
|
+
build_step = strained_material.metadata.build[-1]
|
|
35
|
+
assert build_step.configuration["scale_factor"] == 1.1
|
|
36
|
+
assert build_step.configuration["strain_matrix"] == [[1.1, 0.0, 0.0], [0.0, 1.1, 0.0], [0.0, 0.0, 1.1]]
|