@mat3ra/made 2025.4.4-0 → 2025.4.7-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/dist/js/basis/basis.d.ts +0 -53
- package/dist/js/basis/basis.js +1 -54
- package/package.json +1 -1
- package/pyproject.toml +1 -0
- package/src/js/basis/basis.ts +1 -54
- package/src/py/mat3ra/made/basis/__init__.py +14 -0
- package/src/py/mat3ra/made/cell.py +37 -40
- package/src/py/mat3ra/made/lattice.py +10 -28
- package/src/py/mat3ra/made/material.py +2 -2
- package/src/py/mat3ra/made/tools/build/defect/builders.py +7 -7
- package/src/py/mat3ra/made/tools/build/utils.py +9 -10
- package/tests/fixtures/C2H4-translated.json +2 -2
- package/tests/fixtures/C2H4.json +2 -2
- package/tests/fixtures/Graphene.json +2 -2
- package/tests/fixtures/H2+H-final.json +2 -2
- package/tests/fixtures/H2+H-image.json +2 -2
- package/tests/fixtures/H2+H-initial.json +2 -2
- package/tests/fixtures/Na4Cl4-cartesian.json +2 -2
- package/tests/fixtures/Na4Cl4.json +2 -2
- package/tests/fixtures/Ni-hex.json +2 -2
- package/tests/fixtures/Si-hex.json +2 -2
- package/tests/fixtures/Si-slab.json +2 -2
- package/tests/fixtures/Si2-basis-repeated.json +2 -2
- package/tests/js/basis/basis.js +7 -4
- package/tests/py/unit/fixtures/cuts.py +118 -0
- package/tests/py/unit/test_cell.py +104 -0
- package/tests/py/unit/test_lattice.py +10 -8
- package/tests/py/unit/test_material.py +15 -1
- package/tests/py/unit/test_tools_build.py +26 -44
- package/tests/py/unit/test_tools_build_defect.py +8 -1
- package/tests/py/unit/test_tools_build_grain_boundary.py +1 -1
- package/tests/py/unit/test_tools_build_interface.py +1 -1
package/dist/js/basis/basis.d.ts
CHANGED
|
@@ -58,59 +58,6 @@ export declare class Basis {
|
|
|
58
58
|
static get unitsOptionsConfig(): typeof ATOMIC_COORD_UNITS;
|
|
59
59
|
static get unitsOptionsDefaultValue(): string;
|
|
60
60
|
static get defaultCell(): [import("@mat3ra/esse/dist/js/types").ArrayOf3NumberElementsSchema, import("@mat3ra/esse/dist/js/types").ArrayOf3NumberElementsSchema, import("@mat3ra/esse/dist/js/types").ArrayOf3NumberElementsSchema];
|
|
61
|
-
/**
|
|
62
|
-
* Serialize class instance to JSON.
|
|
63
|
-
* @param skipRounding - Whether to skip rounding the resulting lattice values, defaults to `false`.
|
|
64
|
-
* @example As below:
|
|
65
|
-
{
|
|
66
|
-
"elements" : [
|
|
67
|
-
{
|
|
68
|
-
"id" : 0,
|
|
69
|
-
"value" : "Si"
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
"id" : 1,
|
|
73
|
-
"value" : "Si"
|
|
74
|
-
}
|
|
75
|
-
],
|
|
76
|
-
"coordinates" : [
|
|
77
|
-
{
|
|
78
|
-
"id" : 0,
|
|
79
|
-
"value" : [
|
|
80
|
-
0,
|
|
81
|
-
0,
|
|
82
|
-
0
|
|
83
|
-
]
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
"id" : 1,
|
|
87
|
-
"value" : [
|
|
88
|
-
0.25,
|
|
89
|
-
0.25,
|
|
90
|
-
0.25
|
|
91
|
-
]
|
|
92
|
-
}
|
|
93
|
-
],
|
|
94
|
-
"units" : "crystal",
|
|
95
|
-
"cell" : [
|
|
96
|
-
[
|
|
97
|
-
1,
|
|
98
|
-
0,
|
|
99
|
-
0
|
|
100
|
-
],
|
|
101
|
-
[
|
|
102
|
-
0,
|
|
103
|
-
1,
|
|
104
|
-
0
|
|
105
|
-
],
|
|
106
|
-
[
|
|
107
|
-
0,
|
|
108
|
-
0,
|
|
109
|
-
1
|
|
110
|
-
]
|
|
111
|
-
]
|
|
112
|
-
}
|
|
113
|
-
*/
|
|
114
61
|
toJSON(skipRounding?: boolean): BasisSchema;
|
|
115
62
|
/** Return coordinates rounded to default precision */
|
|
116
63
|
get coordinatesRounded(): {
|
package/dist/js/basis/basis.js
CHANGED
|
@@ -43,65 +43,11 @@ class Basis {
|
|
|
43
43
|
static get defaultCell() {
|
|
44
44
|
return new lattice_1.Lattice().vectorArrays;
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Serialize class instance to JSON.
|
|
48
|
-
* @param skipRounding - Whether to skip rounding the resulting lattice values, defaults to `false`.
|
|
49
|
-
* @example As below:
|
|
50
|
-
{
|
|
51
|
-
"elements" : [
|
|
52
|
-
{
|
|
53
|
-
"id" : 0,
|
|
54
|
-
"value" : "Si"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"id" : 1,
|
|
58
|
-
"value" : "Si"
|
|
59
|
-
}
|
|
60
|
-
],
|
|
61
|
-
"coordinates" : [
|
|
62
|
-
{
|
|
63
|
-
"id" : 0,
|
|
64
|
-
"value" : [
|
|
65
|
-
0,
|
|
66
|
-
0,
|
|
67
|
-
0
|
|
68
|
-
]
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
"id" : 1,
|
|
72
|
-
"value" : [
|
|
73
|
-
0.25,
|
|
74
|
-
0.25,
|
|
75
|
-
0.25
|
|
76
|
-
]
|
|
77
|
-
}
|
|
78
|
-
],
|
|
79
|
-
"units" : "crystal",
|
|
80
|
-
"cell" : [
|
|
81
|
-
[
|
|
82
|
-
1,
|
|
83
|
-
0,
|
|
84
|
-
0
|
|
85
|
-
],
|
|
86
|
-
[
|
|
87
|
-
0,
|
|
88
|
-
1,
|
|
89
|
-
0
|
|
90
|
-
],
|
|
91
|
-
[
|
|
92
|
-
0,
|
|
93
|
-
0,
|
|
94
|
-
1
|
|
95
|
-
]
|
|
96
|
-
]
|
|
97
|
-
}
|
|
98
|
-
*/
|
|
99
46
|
toJSON(skipRounding = false) {
|
|
100
47
|
const json = {
|
|
101
48
|
elements: this.elements,
|
|
102
49
|
coordinates: skipRounding ? this.coordinates : this.coordinatesRounded,
|
|
103
50
|
units: this.units,
|
|
104
|
-
cell: skipRounding ? this.cell : this.cellRounded,
|
|
105
51
|
};
|
|
106
52
|
if (!underscore_1.default.isEmpty(this.labels)) {
|
|
107
53
|
return JSON.parse(JSON.stringify({
|
|
@@ -131,6 +77,7 @@ class Basis {
|
|
|
131
77
|
clone(extraContext) {
|
|
132
78
|
return new this.constructor({
|
|
133
79
|
...this.toJSON(),
|
|
80
|
+
cell: this.cell,
|
|
134
81
|
...extraContext,
|
|
135
82
|
});
|
|
136
83
|
}
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/js/basis/basis.ts
CHANGED
|
@@ -99,65 +99,11 @@ export class Basis {
|
|
|
99
99
|
return new Lattice().vectorArrays;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
/**
|
|
103
|
-
* Serialize class instance to JSON.
|
|
104
|
-
* @param skipRounding - Whether to skip rounding the resulting lattice values, defaults to `false`.
|
|
105
|
-
* @example As below:
|
|
106
|
-
{
|
|
107
|
-
"elements" : [
|
|
108
|
-
{
|
|
109
|
-
"id" : 0,
|
|
110
|
-
"value" : "Si"
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
"id" : 1,
|
|
114
|
-
"value" : "Si"
|
|
115
|
-
}
|
|
116
|
-
],
|
|
117
|
-
"coordinates" : [
|
|
118
|
-
{
|
|
119
|
-
"id" : 0,
|
|
120
|
-
"value" : [
|
|
121
|
-
0,
|
|
122
|
-
0,
|
|
123
|
-
0
|
|
124
|
-
]
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
"id" : 1,
|
|
128
|
-
"value" : [
|
|
129
|
-
0.25,
|
|
130
|
-
0.25,
|
|
131
|
-
0.25
|
|
132
|
-
]
|
|
133
|
-
}
|
|
134
|
-
],
|
|
135
|
-
"units" : "crystal",
|
|
136
|
-
"cell" : [
|
|
137
|
-
[
|
|
138
|
-
1,
|
|
139
|
-
0,
|
|
140
|
-
0
|
|
141
|
-
],
|
|
142
|
-
[
|
|
143
|
-
0,
|
|
144
|
-
1,
|
|
145
|
-
0
|
|
146
|
-
],
|
|
147
|
-
[
|
|
148
|
-
0,
|
|
149
|
-
0,
|
|
150
|
-
1
|
|
151
|
-
]
|
|
152
|
-
]
|
|
153
|
-
}
|
|
154
|
-
*/
|
|
155
102
|
toJSON(skipRounding = false): BasisSchema {
|
|
156
103
|
const json = {
|
|
157
104
|
elements: this.elements,
|
|
158
105
|
coordinates: skipRounding ? this.coordinates : this.coordinatesRounded,
|
|
159
106
|
units: this.units,
|
|
160
|
-
cell: skipRounding ? this.cell : this.cellRounded,
|
|
161
107
|
};
|
|
162
108
|
|
|
163
109
|
if (!_.isEmpty(this.labels)) {
|
|
@@ -194,6 +140,7 @@ export class Basis {
|
|
|
194
140
|
clone(extraContext?: Partial<BasisProps>): Basis {
|
|
195
141
|
return new (this.constructor as typeof Basis)({
|
|
196
142
|
...this.toJSON(),
|
|
143
|
+
cell: this.cell,
|
|
197
144
|
...extraContext,
|
|
198
145
|
});
|
|
199
146
|
}
|
|
@@ -26,6 +26,8 @@ class Basis(BasisSchema, InMemoryEntityPydantic):
|
|
|
26
26
|
kwargs["labels"] = ArrayWithIds.from_list_of_dicts(kwargs["labels"])
|
|
27
27
|
if isinstance(kwargs.get("constraints"), list):
|
|
28
28
|
kwargs["constraints"] = ArrayWithIds.from_list_of_dicts(kwargs["constraints"])
|
|
29
|
+
if isinstance(kwargs.get("cell"), list):
|
|
30
|
+
kwargs["cell"] = Cell.from_vectors_array(kwargs["cell"])
|
|
29
31
|
return kwargs
|
|
30
32
|
|
|
31
33
|
def __init__(self, *args: Any, **kwargs: Any):
|
|
@@ -110,6 +112,18 @@ class Basis(BasisSchema, InMemoryEntityPydantic):
|
|
|
110
112
|
self.elements.add_item(element)
|
|
111
113
|
self.coordinates.add_item(coordinate)
|
|
112
114
|
|
|
115
|
+
def add_atoms_from_another_basis(self, other_basis: "Basis"):
|
|
116
|
+
"""
|
|
117
|
+
Add atoms from another basis to this basis.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
other_basis (Basis): The other basis to add atoms from.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
self.elements.add_items(other_basis.elements.values)
|
|
124
|
+
self.coordinates.add_items(other_basis.coordinates.values)
|
|
125
|
+
self.labels.add_items(other_basis.labels.values)
|
|
126
|
+
|
|
113
127
|
def remove_atom_by_id(self, id: int):
|
|
114
128
|
self.elements.remove_item(id)
|
|
115
129
|
self.coordinates.remove_item(id)
|
|
@@ -1,58 +1,55 @@
|
|
|
1
|
-
from typing import List
|
|
1
|
+
from typing import List
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
|
+
from mat3ra.code.vector import RoundedVector3D
|
|
5
|
+
from mat3ra.esse.models.properties_directory.structural.lattice.lattice_vectors import LatticeExplicitUnit as CellSchema
|
|
4
6
|
from mat3ra.utils.mixins import RoundNumericValuesMixin
|
|
5
|
-
from pydantic import
|
|
7
|
+
from pydantic import Field
|
|
6
8
|
|
|
9
|
+
DEFAULT_CELL = np.eye(3).tolist()
|
|
7
10
|
|
|
8
|
-
class Cell(RoundNumericValuesMixin, BaseModel):
|
|
9
|
-
# TODO: figure out how to use ArrayOf3NumberElementsSchema
|
|
10
|
-
vector1: List[float] = Field(default_factory=lambda: [1.0, 0.0, 0.0])
|
|
11
|
-
vector2: List[float] = Field(default_factory=lambda: [0.0, 1.0, 0.0])
|
|
12
|
-
vector3: List[float] = Field(default_factory=lambda: [0.0, 0.0, 1.0])
|
|
13
|
-
__round_precision__ = 6
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
vectors_array = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
|
|
19
|
-
|
|
20
|
-
# Ensure vectors are properly converted to lists of floats
|
|
21
|
-
processed_vectors = []
|
|
22
|
-
for vector in vectors_array:
|
|
23
|
-
processed_vector = [float(v) for v in vector]
|
|
24
|
-
processed_vectors.append(processed_vector)
|
|
12
|
+
class Cell(RoundNumericValuesMixin, CellSchema):
|
|
13
|
+
__rounded_vector3d__ = RoundedVector3D
|
|
14
|
+
__default_vectors__ = DEFAULT_CELL
|
|
25
15
|
|
|
26
|
-
|
|
16
|
+
a: RoundedVector3D = Field(default_factory=lambda: Cell.__rounded_vector3d__(DEFAULT_CELL[0]))
|
|
17
|
+
b: RoundedVector3D = Field(default_factory=lambda: Cell.__rounded_vector3d__(DEFAULT_CELL[1]))
|
|
18
|
+
c: RoundedVector3D = Field(default_factory=lambda: Cell.__rounded_vector3d__(DEFAULT_CELL[2]))
|
|
27
19
|
|
|
28
|
-
@
|
|
29
|
-
def
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_vectors_array(cls, vectors: List[List[float]] = DEFAULT_CELL) -> "Cell":
|
|
22
|
+
return cls(
|
|
23
|
+
a=RoundedVector3D(vectors[0]),
|
|
24
|
+
b=RoundedVector3D(vectors[1]),
|
|
25
|
+
c=RoundedVector3D(vectors[2]),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def get_vector_arrays(self, skip_rounding=False) -> List[List[float]]:
|
|
30
29
|
if skip_rounding:
|
|
31
|
-
return [self.
|
|
32
|
-
return
|
|
30
|
+
return [self.a.value, self.b.value, self.c.value]
|
|
31
|
+
return [self.a.value_rounded, self.b.value_rounded, self.c.value_rounded]
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return
|
|
37
|
-
self.vector1 if skip_rounding else _(self.vector1),
|
|
38
|
-
self.vector2 if skip_rounding else _(self.vector2),
|
|
39
|
-
self.vector3 if skip_rounding else _(self.vector3),
|
|
40
|
-
]
|
|
33
|
+
@property
|
|
34
|
+
def vector_arrays(self) -> List[List[float]]:
|
|
35
|
+
return self.get_vector_arrays(skip_rounding=True)
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
@property
|
|
38
|
+
def vector_arrays_rounded(self) -> List[List[float]]:
|
|
39
|
+
return self.get_vector_arrays(skip_rounding=False)
|
|
44
40
|
|
|
45
41
|
def convert_point_to_cartesian(self, point: List[float]) -> List[float]:
|
|
46
|
-
np_vector = np.array(self.
|
|
47
|
-
|
|
48
|
-
return self.round_array_or_number(result_list)
|
|
42
|
+
np_vector = np.array(self.vector_arrays)
|
|
43
|
+
return np.dot(point, np_vector).tolist()
|
|
49
44
|
|
|
50
45
|
def convert_point_to_crystal(self, point: List[float]) -> List[float]:
|
|
51
|
-
np_vector = np.array(self.
|
|
52
|
-
|
|
53
|
-
return self.round_array_or_number(result_list)
|
|
46
|
+
np_vector = np.array(self.vector_arrays)
|
|
47
|
+
return np.dot(point, np.linalg.inv(np_vector)).tolist()
|
|
54
48
|
|
|
55
49
|
@property
|
|
56
50
|
def volume(self) -> float:
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
return np.linalg.det(np.array(self.vector_arrays))
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def volume_rounded(self) -> float:
|
|
55
|
+
return self.round_array_or_number(self.volume)
|
|
@@ -3,7 +3,6 @@ from typing import List, Optional
|
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
from mat3ra.code.entity import InMemoryEntityPydantic
|
|
6
|
-
from mat3ra.code.vector import RoundedVector3D
|
|
7
6
|
from mat3ra.esse.models.properties_directory.structural.lattice.lattice_bravais import (
|
|
8
7
|
LatticeImplicitSchema as LatticeBravaisSchema,
|
|
9
8
|
)
|
|
@@ -11,35 +10,17 @@ from mat3ra.esse.models.properties_directory.structural.lattice.lattice_bravais
|
|
|
11
10
|
LatticeTypeEnum,
|
|
12
11
|
LatticeUnitsSchema,
|
|
13
12
|
)
|
|
14
|
-
from mat3ra.esse.models.properties_directory.structural.lattice.lattice_vectors import (
|
|
15
|
-
LatticeExplicitUnit as LatticeVectorsSchema,
|
|
16
|
-
)
|
|
17
13
|
from mat3ra.utils.mixins import RoundNumericValuesMixin
|
|
18
|
-
from pydantic import Field
|
|
19
14
|
|
|
20
15
|
from .cell import Cell
|
|
21
16
|
|
|
22
17
|
COORDINATE_TOLERANCE = 6
|
|
23
18
|
|
|
24
19
|
|
|
25
|
-
class
|
|
20
|
+
class LatticeVectors(Cell):
|
|
26
21
|
pass
|
|
27
22
|
|
|
28
23
|
|
|
29
|
-
class LatticeVectors(RoundNumericValuesMixin, LatticeVectorsSchema):
|
|
30
|
-
"""
|
|
31
|
-
A class to represent the lattice vectors.
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
a: LatticeVector = Field(default_factory=lambda: LatticeVector(root=[1.0, 0.0, 0.0]))
|
|
35
|
-
b: LatticeVector = Field(default_factory=lambda: LatticeVector(root=[0.0, 1.0, 0.0]))
|
|
36
|
-
c: LatticeVector = Field(default_factory=lambda: LatticeVector(root=[0.0, 0.0, 1.0]))
|
|
37
|
-
|
|
38
|
-
@classmethod
|
|
39
|
-
def from_vectors_array(cls, vectors: List[List[float]]) -> "LatticeVectors":
|
|
40
|
-
return cls(a=LatticeVector(root=vectors[0]), b=LatticeVector(root=vectors[1]), c=LatticeVector(root=vectors[2]))
|
|
41
|
-
|
|
42
|
-
|
|
43
24
|
class Lattice(RoundNumericValuesMixin, LatticeBravaisSchema, InMemoryEntityPydantic):
|
|
44
25
|
__types__ = LatticeTypeEnum
|
|
45
26
|
__type_default__ = LatticeBravaisSchema.model_fields["type"].default
|
|
@@ -111,19 +92,20 @@ class Lattice(RoundNumericValuesMixin, LatticeBravaisSchema, InMemoryEntityPydan
|
|
|
111
92
|
)
|
|
112
93
|
|
|
113
94
|
@property
|
|
114
|
-
def vector_arrays(self
|
|
115
|
-
|
|
116
|
-
if not skip_rounding:
|
|
117
|
-
return list(map(lambda vector: vector.value_rounded, _))
|
|
118
|
-
return list(map(lambda vector: vector.root, _))
|
|
95
|
+
def vector_arrays(self) -> List[List[float]]:
|
|
96
|
+
return self.vectors.vector_arrays
|
|
119
97
|
|
|
120
98
|
@property
|
|
121
|
-
def
|
|
122
|
-
return
|
|
99
|
+
def vector_arrays_rounded(self) -> List[List[float]]:
|
|
100
|
+
return self.vectors.vector_arrays_rounded
|
|
123
101
|
|
|
124
102
|
@property
|
|
125
103
|
def cell_volume(self) -> float:
|
|
126
|
-
return self.
|
|
104
|
+
return self.vectors.volume
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def cell_volume_rounded(self) -> float:
|
|
108
|
+
return self.vectors.volume_rounded
|
|
127
109
|
|
|
128
110
|
def get_scaled_by_matrix(self, matrix: List[List[float]]):
|
|
129
111
|
"""
|
|
@@ -61,7 +61,7 @@ class Material(MaterialSchema, HasDescriptionHasMetadataNamedDefaultableInMemory
|
|
|
61
61
|
def model_post_init(self, __context: Any) -> None:
|
|
62
62
|
if not self.name and self.formula:
|
|
63
63
|
self.name: str = self.formula
|
|
64
|
-
self.basis.cell = self.lattice.
|
|
64
|
+
self.basis.cell = self.lattice.vectors
|
|
65
65
|
|
|
66
66
|
@property
|
|
67
67
|
def coordinates_array(self) -> List[List[float]]:
|
|
@@ -82,7 +82,7 @@ class Material(MaterialSchema, HasDescriptionHasMetadataNamedDefaultableInMemory
|
|
|
82
82
|
original_is_in_crystal_units = self.basis.is_in_crystal_units
|
|
83
83
|
self.to_cartesian()
|
|
84
84
|
self.lattice = Lattice.from_vectors_array([lattice_vector1, lattice_vector2, lattice_vector3])
|
|
85
|
-
self.basis.cell = self.lattice.
|
|
85
|
+
self.basis.cell = self.lattice.vectors
|
|
86
86
|
if original_is_in_crystal_units:
|
|
87
87
|
self.to_crystal()
|
|
88
88
|
|
|
@@ -612,7 +612,7 @@ class TerraceSlabDefectBuilder(SlabDefectBuilder):
|
|
|
612
612
|
The normalized cut direction vector in Cartesian coordinates.
|
|
613
613
|
"""
|
|
614
614
|
np_cut_direction = np.array(cut_direction)
|
|
615
|
-
direction_vector = np.dot(np.array(material.basis.cell.
|
|
615
|
+
direction_vector = np.dot(np.array(material.basis.cell.vector_arrays), np_cut_direction)
|
|
616
616
|
normalized_direction_vector = direction_vector / np.linalg.norm(direction_vector)
|
|
617
617
|
return normalized_direction_vector
|
|
618
618
|
|
|
@@ -648,7 +648,7 @@ class TerraceSlabDefectBuilder(SlabDefectBuilder):
|
|
|
648
648
|
"""
|
|
649
649
|
height_cartesian = self._calculate_height_cartesian(original_material, new_material)
|
|
650
650
|
cut_direction_xy_proj_cart = np.linalg.norm(
|
|
651
|
-
np.dot(np.array(new_material.
|
|
651
|
+
np.dot(np.array(new_material.lattice.vector_arrays), normalized_direction_vector)
|
|
652
652
|
)
|
|
653
653
|
# Slope of the terrace along the cut direction
|
|
654
654
|
hypotenuse = np.linalg.norm([height_cartesian, cut_direction_xy_proj_cart])
|
|
@@ -679,17 +679,17 @@ class TerraceSlabDefectBuilder(SlabDefectBuilder):
|
|
|
679
679
|
Returns:
|
|
680
680
|
The material with the increased lattice size.
|
|
681
681
|
"""
|
|
682
|
-
vector_a, vector_b =
|
|
683
|
-
norm_a, norm_b =
|
|
682
|
+
vector_a, vector_b = material.lattice.vectors.a, material.lattice.vectors.b
|
|
683
|
+
norm_a, norm_b = vector_a.norm, vector_b.norm
|
|
684
684
|
|
|
685
|
-
delta_a_cart = np.dot(vector_a, np.array(direction_of_increase)) * length_increase / norm_a
|
|
686
|
-
delta_b_cart = np.dot(vector_b, np.array(direction_of_increase)) * length_increase / norm_b
|
|
685
|
+
delta_a_cart = np.dot(vector_a.value, np.array(direction_of_increase)) * length_increase / norm_a
|
|
686
|
+
delta_b_cart = np.dot(vector_b.value, np.array(direction_of_increase)) * length_increase / norm_b
|
|
687
687
|
|
|
688
688
|
scaling_matrix = np.eye(3)
|
|
689
689
|
scaling_matrix[0, 0] += delta_a_cart / norm_a
|
|
690
690
|
scaling_matrix[1, 1] += delta_b_cart / norm_b
|
|
691
691
|
|
|
692
|
-
new_lattice = material.lattice.get_scaled_by_matrix(scaling_matrix)
|
|
692
|
+
new_lattice = material.lattice.get_scaled_by_matrix(scaling_matrix.tolist())
|
|
693
693
|
material.set_lattice(new_lattice)
|
|
694
694
|
return material
|
|
695
695
|
|
|
@@ -42,16 +42,15 @@ def merge_two_bases(basis1: Basis, basis2: Basis, distance_tolerance: float) ->
|
|
|
42
42
|
merged_elements_values = basis1.elements.values + basis2.elements.values
|
|
43
43
|
merged_coordinates_values = basis1.coordinates.values + basis2.coordinates.values
|
|
44
44
|
merged_labels_values = basis1.labels.values + basis2.labels.values if basis1.labels and basis2.labels else []
|
|
45
|
+
merged_constraints_values = basis1.constraints.values + basis2.constraints.values
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
)
|
|
54
|
-
resolved_basis = resolve_close_coordinates_basis(merged_basis, distance_tolerance)
|
|
47
|
+
new_basis = basis1.clone()
|
|
48
|
+
new_basis.elements = ArrayWithIds.from_values(values=merged_elements_values)
|
|
49
|
+
new_basis.coordinates = Coordinates.from_values(values=merged_coordinates_values)
|
|
50
|
+
new_basis.labels = ArrayWithIds.from_values(values=merged_labels_values)
|
|
51
|
+
new_basis.constraints = ArrayWithIds.from_values(values=merged_constraints_values)
|
|
52
|
+
|
|
53
|
+
resolved_basis = resolve_close_coordinates_basis(new_basis, distance_tolerance)
|
|
55
54
|
|
|
56
55
|
return resolved_basis
|
|
57
56
|
|
|
@@ -79,7 +78,7 @@ def merge_two_materials(
|
|
|
79
78
|
def merge_materials(
|
|
80
79
|
materials: List[Material],
|
|
81
80
|
material_name: Optional[str] = None,
|
|
82
|
-
distance_tolerance: float = 0.
|
|
81
|
+
distance_tolerance: float = 0.1,
|
|
83
82
|
merge_dangerously=False,
|
|
84
83
|
) -> Material:
|
|
85
84
|
"""
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:29c040a13aaea2e8317a34dc292abc52004394b746c83f5b7594818e1d736355
|
|
3
|
+
size 1669
|
package/tests/fixtures/C2H4.json
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:4ad6e240bbc204fc7ea8e92cd74e27de38a7109f542fbd5deecfca52bd32cee9
|
|
3
|
+
size 1649
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:3fbf76a64fe44dccbad8e5cbd625e7b3a0fe3f0496c0fd447622632dca0fde62
|
|
3
|
+
size 1733
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:e360cab9de055b791907fd1c63f625756bd90d2e77b7d136032601275b796c06
|
|
3
|
+
size 1114
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:84f42fb06dba054dec55a28ecb6c5d976a07daff25ea86b341ceab7cc513afe3
|
|
3
|
+
size 1116
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:abe820a78f0482aee9418df39b20fdea266117a68e631567634725b02315d883
|
|
3
|
+
size 1114
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:1914573cd9f1bb2e0161d07581125acfeaa9804c6f2285e8c9ef76c83060c867
|
|
3
|
+
size 2137
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:8d4c3c8db449d2dae640888f6474cb04d92f4b98f392c14866d867d1d41517ad
|
|
3
|
+
size 1990
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:0ac714e6f565c9bf4dd30c291a2e13d67da441b07cb5dcf6bb618537596702de
|
|
3
|
+
size 1870
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:2dc9f03937e4f004274d7dafbb1e06892ef9f425b6f452fa32a43fc191f783cd
|
|
3
|
+
size 2062
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:cb8fc82009d8a5c6d437bf17301e7a99760b51f756b4867360d48eaf5b7a7505
|
|
3
|
+
size 1588
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:025bb26b92bb4b12eb68169e1e888baf847d4b9326538e5494ec8eace7b8cf18
|
|
3
|
+
size 654
|
package/tests/js/basis/basis.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { expect } from "chai";
|
|
2
2
|
|
|
3
3
|
import { Basis } from "../../../src/js/basis/basis";
|
|
4
|
+
import { Material } from "../../../src/js/material";
|
|
4
5
|
import {
|
|
5
6
|
AsGeBasis,
|
|
6
7
|
C2H4,
|
|
@@ -21,8 +22,10 @@ describe("Basis", () => {
|
|
|
21
22
|
});
|
|
22
23
|
|
|
23
24
|
it("should return true when basis is compared to its clone", () => {
|
|
24
|
-
const
|
|
25
|
-
|
|
25
|
+
const basis1 = new Material(Na4Cl4).Basis;
|
|
26
|
+
const basis2 = basis1.clone();
|
|
27
|
+
expect(basis1.isEqualTo(basis2)).to.be.equal(true);
|
|
28
|
+
expect(basis1.hasEquivalentCellTo(basis2)).to.be.equal(true);
|
|
26
29
|
});
|
|
27
30
|
|
|
28
31
|
it("should return jsonified basis", () => {
|
|
@@ -31,8 +34,8 @@ describe("Basis", () => {
|
|
|
31
34
|
});
|
|
32
35
|
|
|
33
36
|
it("should return true if cells are equal", () => {
|
|
34
|
-
const basis1 = new
|
|
35
|
-
const basis2 = new
|
|
37
|
+
const basis1 = new Material(Na4Cl4).Basis;
|
|
38
|
+
const basis2 = new Material(Na4Cl4Cartesian).Basis;
|
|
36
39
|
expect(basis1.hasEquivalentCellTo(basis2)).to.be.equal(true);
|
|
37
40
|
});
|
|
38
41
|
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
FULL_MATERIAL = {
|
|
2
|
+
"basis": {
|
|
3
|
+
"constraints": [],
|
|
4
|
+
"coordinates": [
|
|
5
|
+
{"id": 0, "value": [0.0, 0.0, 0.0]},
|
|
6
|
+
{"id": 1, "value": [0.0, 0.5, 0.5]},
|
|
7
|
+
{"id": 2, "value": [0.5, 0.0, 0.5]},
|
|
8
|
+
{"id": 3, "value": [0.5, 0.5, 0.0]},
|
|
9
|
+
],
|
|
10
|
+
"elements": [
|
|
11
|
+
{"id": 0, "value": "Ni"},
|
|
12
|
+
{"id": 1, "value": "Ni"},
|
|
13
|
+
{"id": 2, "value": "Ni"},
|
|
14
|
+
{"id": 3, "value": "Ni"},
|
|
15
|
+
],
|
|
16
|
+
"labels": [],
|
|
17
|
+
"units": "crystal",
|
|
18
|
+
},
|
|
19
|
+
"consistencyChecks": None,
|
|
20
|
+
"derivedProperties": None,
|
|
21
|
+
"description": None,
|
|
22
|
+
"descriptionObject": None,
|
|
23
|
+
"external": None,
|
|
24
|
+
"field_id": None,
|
|
25
|
+
"formula": None,
|
|
26
|
+
"icsdId": None,
|
|
27
|
+
"isDefault": False,
|
|
28
|
+
"isNonPeriodic": False,
|
|
29
|
+
"lattice": {
|
|
30
|
+
"a": 3.52,
|
|
31
|
+
"alpha": 90.0,
|
|
32
|
+
"b": 3.52,
|
|
33
|
+
"beta": 90.0,
|
|
34
|
+
"c": 3.52,
|
|
35
|
+
"gamma": 90.0,
|
|
36
|
+
"type": "TRI",
|
|
37
|
+
"units": {"angle": "degree", "length": "angstrom"},
|
|
38
|
+
},
|
|
39
|
+
"metadata": {"boundaryConditions": {"offset": 0, "type": "pbc"}},
|
|
40
|
+
"name": "",
|
|
41
|
+
"scaledHash": None,
|
|
42
|
+
"schemaVersion": "2022.8.16",
|
|
43
|
+
"slug": None,
|
|
44
|
+
"src": None,
|
|
45
|
+
"systemName": None,
|
|
46
|
+
"unitCellFormula": None,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
CAVITY_MATERIAL_BASIS = {
|
|
50
|
+
"basis": {
|
|
51
|
+
"constraints": [],
|
|
52
|
+
"coordinates": [
|
|
53
|
+
{"id": 1, "value": [0.0, 0.5, 0.5]},
|
|
54
|
+
{"id": 2, "value": [0.5, 0.0, 0.5]},
|
|
55
|
+
{"id": 4, "value": [0.5, 0.5, 0.0]},
|
|
56
|
+
],
|
|
57
|
+
"elements": [{"id": 1, "value": "Ni"}, {"id": 2, "value": "Ni"}, {"id": 4, "value": "Au"}],
|
|
58
|
+
"labels": [],
|
|
59
|
+
"units": "crystal",
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
SECTION_MATERIAL_BASIS = {
|
|
65
|
+
"basis": {
|
|
66
|
+
"constraints": [],
|
|
67
|
+
"coordinates": [{"id": 0, "value": [0.0, 0.0, 0.0]}, {"id": 3, "value": [0.5, 0.5, 0.0]}],
|
|
68
|
+
"elements": [{"id": 0, "value": "Ni"}, {"id": 3, "value": "Ni"}],
|
|
69
|
+
"labels": [],
|
|
70
|
+
"units": "crystal",
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
SECTION_MATERIAL_BASIS_EXTRA_ATOM = {
|
|
75
|
+
"basis": {
|
|
76
|
+
"constraints": [],
|
|
77
|
+
"coordinates": [
|
|
78
|
+
{"id": 0, "value": [0.0, 0.0, 0.0]},
|
|
79
|
+
{"id": 3, "value": [0.5, 0.5, 0.0]},
|
|
80
|
+
{"id": 4, "value": [0.51, 0.51, 0.0]}, # Extra atom collides with Au in cavity material
|
|
81
|
+
],
|
|
82
|
+
"elements": [{"id": 0, "value": "Ni"}, {"id": 3, "value": "Ni"}, {"id": 4, "value": "O"}],
|
|
83
|
+
"labels": [],
|
|
84
|
+
"units": "crystal",
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
MERGED_SECTION_CAVITY_BASIS = {
|
|
89
|
+
"elements": [
|
|
90
|
+
{"id": 0, "value": "Ni"},
|
|
91
|
+
{"id": 1, "value": "Ni"},
|
|
92
|
+
{"id": 2, "value": "Ni"},
|
|
93
|
+
{"id": 4, "value": "Au"},
|
|
94
|
+
],
|
|
95
|
+
"coordinates": [
|
|
96
|
+
{"id": 0, "value": [0.0, 0.0, 0.0]},
|
|
97
|
+
{"id": 1, "value": [0.0, 0.5, 0.5]},
|
|
98
|
+
{"id": 2, "value": [0.5, 0.0, 0.5]},
|
|
99
|
+
{"id": 4, "value": [0.5, 0.5, 0.0]},
|
|
100
|
+
],
|
|
101
|
+
"labels": [],
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
MERGED_CAVITY_SECTION_BASIS = {
|
|
105
|
+
"elements": [
|
|
106
|
+
{"id": 1, "value": "Ni"},
|
|
107
|
+
{"id": 2, "value": "Ni"},
|
|
108
|
+
{"id": 0, "value": "Ni"},
|
|
109
|
+
{"id": 3, "value": "Ni"},
|
|
110
|
+
],
|
|
111
|
+
"coordinates": [
|
|
112
|
+
{"id": 1, "value": [0.0, 0.5, 0.5]},
|
|
113
|
+
{"id": 2, "value": [0.5, 0.0, 0.5]},
|
|
114
|
+
{"id": 0, "value": [0.0, 0.0, 0.0]},
|
|
115
|
+
{"id": 3, "value": [0.5, 0.5, 0.0]},
|
|
116
|
+
],
|
|
117
|
+
"labels": [],
|
|
118
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from mat3ra.code.vector import RoundedVector3D
|
|
2
|
+
from mat3ra.made.cell import Cell
|
|
3
|
+
|
|
4
|
+
VECTORS = [
|
|
5
|
+
[1.0, 0.0, 0.0],
|
|
6
|
+
[0.0, 2.0, 0.0],
|
|
7
|
+
[0.0, 0.0, 3.0],
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
VECTORS_VOLUME = 6.0
|
|
11
|
+
|
|
12
|
+
VECTORS_EQUAL_UP_TO_PRECISION_4 = [
|
|
13
|
+
[1.00001, 0.0, 0.0],
|
|
14
|
+
[0.0, 2.00001, 0.0],
|
|
15
|
+
[0.0, 0.0, 3.00001],
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME = 6.000110000600002
|
|
19
|
+
VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME_ROUNDED_TO_PRECISION_4 = 6.0001
|
|
20
|
+
VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME_ROUNDED_TO_PRECISION_5 = 6.00011
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_cell_creation():
|
|
24
|
+
cell = Cell(a=RoundedVector3D(VECTORS[0]), b=RoundedVector3D(VECTORS[1]), c=RoundedVector3D(VECTORS[2]))
|
|
25
|
+
assert cell.a.value == VECTORS[0]
|
|
26
|
+
assert cell.b.value == VECTORS[1]
|
|
27
|
+
assert cell.c.value == VECTORS[2]
|
|
28
|
+
assert cell.volume == 6.0
|
|
29
|
+
assert cell.vector_arrays == VECTORS
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_cell_creation_default():
|
|
33
|
+
cell = Cell()
|
|
34
|
+
assert cell.a.value == Cell.__default_vectors__[0]
|
|
35
|
+
assert cell.b.value == Cell.__default_vectors__[1]
|
|
36
|
+
assert cell.c.value == Cell.__default_vectors__[2]
|
|
37
|
+
assert cell.volume == 1.0
|
|
38
|
+
assert cell.vector_arrays == Cell.__default_vectors__
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_from_vectors_array():
|
|
42
|
+
cell = Cell.from_vectors_array(vectors=VECTORS)
|
|
43
|
+
assert cell.a.value == VECTORS[0]
|
|
44
|
+
assert cell.b.value == VECTORS[1]
|
|
45
|
+
assert cell.c.value == VECTORS[2]
|
|
46
|
+
assert cell.volume == VECTORS_VOLUME
|
|
47
|
+
assert cell.vector_arrays == VECTORS
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_get_vector_arrays():
|
|
51
|
+
cell = Cell.from_vectors_array(vectors=VECTORS)
|
|
52
|
+
assert cell.get_vector_arrays() == VECTORS
|
|
53
|
+
assert cell.get_vector_arrays(skip_rounding=True) == VECTORS
|
|
54
|
+
assert cell.get_vector_arrays(skip_rounding=False) == VECTORS
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_vector_arrays_including_rounded():
|
|
58
|
+
class_reference = Cell
|
|
59
|
+
class_reference.__rounded_vector3d__ = RoundedVector3D
|
|
60
|
+
|
|
61
|
+
class_reference.__rounded_vector3d__.__round_precision__ = 4
|
|
62
|
+
cell = Cell.from_vectors_array(vectors=VECTORS_EQUAL_UP_TO_PRECISION_4)
|
|
63
|
+
assert cell.vector_arrays == VECTORS_EQUAL_UP_TO_PRECISION_4
|
|
64
|
+
assert cell.vector_arrays_rounded == VECTORS
|
|
65
|
+
|
|
66
|
+
class_reference.__rounded_vector3d__.__round_precision__ = 5
|
|
67
|
+
cell = Cell.from_vectors_array(vectors=VECTORS_EQUAL_UP_TO_PRECISION_4)
|
|
68
|
+
assert cell.vector_arrays == VECTORS_EQUAL_UP_TO_PRECISION_4
|
|
69
|
+
assert cell.vector_arrays_rounded != VECTORS
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_convert_point_to_cartesian():
|
|
73
|
+
cell = Cell.from_vectors_array(vectors=VECTORS)
|
|
74
|
+
coordinate_in_crystal = [0.5, 0.5, 0.5]
|
|
75
|
+
coordinate_in_cartesian = cell.convert_point_to_cartesian(coordinate_in_crystal)
|
|
76
|
+
expected_coordinate_in_cartesian = [0.5, 1.0, 1.5]
|
|
77
|
+
assert coordinate_in_cartesian == expected_coordinate_in_cartesian
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_convert_point_to_crystal():
|
|
81
|
+
cell = Cell.from_vectors_array(vectors=VECTORS)
|
|
82
|
+
coordinate_in_cartesian = [0.5, 1.0, 1.5]
|
|
83
|
+
coordinate_in_crystal = cell.convert_point_to_crystal(coordinate_in_cartesian)
|
|
84
|
+
expected_coordinate_in_crystal = [0.5, 0.5, 0.5]
|
|
85
|
+
assert coordinate_in_crystal == expected_coordinate_in_crystal
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_volume():
|
|
89
|
+
cell = Cell.from_vectors_array(vectors=VECTORS)
|
|
90
|
+
assert cell.volume == VECTORS_VOLUME
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_volume_rounded():
|
|
94
|
+
class_reference = Cell
|
|
95
|
+
class_reference.__round_precision__ = 4
|
|
96
|
+
cell = class_reference.from_vectors_array(vectors=VECTORS_EQUAL_UP_TO_PRECISION_4)
|
|
97
|
+
assert cell.volume == VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME
|
|
98
|
+
assert cell.volume_rounded == VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME_ROUNDED_TO_PRECISION_4
|
|
99
|
+
|
|
100
|
+
class_reference.__round_precision__ = 5
|
|
101
|
+
cell = class_reference.from_vectors_array(vectors=VECTORS_EQUAL_UP_TO_PRECISION_4)
|
|
102
|
+
assert cell.volume == VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME
|
|
103
|
+
assert cell.volume_rounded != VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME_ROUNDED_TO_PRECISION_4
|
|
104
|
+
assert cell.volume_rounded == VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME_ROUNDED_TO_PRECISION_5
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from mat3ra.
|
|
1
|
+
from mat3ra.code.vector import RoundedVector3D
|
|
2
|
+
from mat3ra.made.lattice import Lattice
|
|
2
3
|
from mat3ra.utils import assertion as assertion_utils
|
|
3
4
|
|
|
4
5
|
DEFAULT_UNITS = Lattice.__units_default__
|
|
@@ -20,16 +21,16 @@ def test_lattice_creation():
|
|
|
20
21
|
def test_lattice_get_vectors():
|
|
21
22
|
lattice = Lattice(a=2.0, b=3.0, c=4.0)
|
|
22
23
|
expected_vectors = [[2.0, 0.0, 0.0], [0.0, 3.0, 0.0], [0.0, 0.0, 4.0]]
|
|
23
|
-
assert expected_vectors == lattice.
|
|
24
|
+
assert expected_vectors == lattice.vector_arrays_rounded
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def test_lattice_vectors_access():
|
|
27
28
|
lattice = Lattice(a=2.0, b=3.0, c=4.0)
|
|
28
29
|
|
|
29
30
|
# Test individual vector access
|
|
30
|
-
assert isinstance(lattice.vectors.a,
|
|
31
|
-
assert isinstance(lattice.vectors.b,
|
|
32
|
-
assert isinstance(lattice.vectors.c,
|
|
31
|
+
assert isinstance(lattice.vectors.a, RoundedVector3D)
|
|
32
|
+
assert isinstance(lattice.vectors.b, RoundedVector3D)
|
|
33
|
+
assert isinstance(lattice.vectors.c, RoundedVector3D)
|
|
33
34
|
|
|
34
35
|
# Test vector arrays access
|
|
35
36
|
arrays = lattice.vector_arrays
|
|
@@ -52,8 +53,10 @@ def test_lattice_from_vectors():
|
|
|
52
53
|
assert lattice.gamma == 90.0
|
|
53
54
|
assert lattice.units == DEFAULT_UNITS
|
|
54
55
|
assert lattice.type.value == DEFAULT_TYPE
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
# Avoid floating point comparison issue
|
|
57
|
+
assertion_utils.assert_deep_almost_equal(lattice.cell_volume, 24.0)
|
|
58
|
+
assert lattice.cell_volume_rounded == 24.0
|
|
59
|
+
assert lattice.vector_arrays_rounded == [[2.0, 0.0, 0.0], [0.0, 3.0, 0.0], [0.0, 0.0, 4.0]]
|
|
57
60
|
|
|
58
61
|
|
|
59
62
|
def test_lattice_get_scaled_by_matrix():
|
|
@@ -69,7 +72,6 @@ def test_lattice_get_scaled_by_matrix():
|
|
|
69
72
|
assert lattice.gamma == 90.0
|
|
70
73
|
assert lattice.units == DEFAULT_UNITS
|
|
71
74
|
assert lattice.type.value == DEFAULT_TYPE
|
|
72
|
-
assert lattice.cell_volume == 27.0
|
|
73
75
|
assertion_utils.assert_deep_almost_equal(lattice.vector_arrays, expected_vector_values)
|
|
74
76
|
|
|
75
77
|
|
|
@@ -23,6 +23,20 @@ def test_create():
|
|
|
23
23
|
assert_two_entities_deep_almost_equal(material, SI_CONVENTIONAL_CELL)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def test_create_with_cell_as_list():
|
|
27
|
+
# The key cell should be ignored and Basis.Cell created from Lattice by Material
|
|
28
|
+
cell = [
|
|
29
|
+
[1.0, 0.0, 0.0],
|
|
30
|
+
[0.0, 1.0, 0.0],
|
|
31
|
+
[0.0, 0.0, 1.0],
|
|
32
|
+
]
|
|
33
|
+
config = {**Material.__default_config__, "basis": {**Material.__default_config__["basis"], "cell": cell}}
|
|
34
|
+
|
|
35
|
+
material = Material.create(config)
|
|
36
|
+
assert isinstance(material.basis, Basis)
|
|
37
|
+
assert material.basis.cell.vector_arrays == material.lattice.vector_arrays
|
|
38
|
+
|
|
39
|
+
|
|
26
40
|
def test_material_to_json():
|
|
27
41
|
material = Material.create_default()
|
|
28
42
|
# Remove all keys that are null in the config
|
|
@@ -74,6 +88,6 @@ def test_basis_cell_lattice_sync():
|
|
|
74
88
|
new_lattice = Lattice.from_vectors_array(new_vectors)
|
|
75
89
|
material.set_lattice(new_lattice)
|
|
76
90
|
# Verify basis.cell matches new lattice vectors
|
|
77
|
-
assertion_utils.assert_deep_almost_equal(new_vectors, material.basis.cell.
|
|
91
|
+
assertion_utils.assert_deep_almost_equal(new_vectors, material.basis.cell.vector_arrays)
|
|
78
92
|
assertion_utils.assert_deep_almost_equal(new_vectors, material.lattice.vector_arrays)
|
|
79
93
|
# Verify basis coordinates are still correct
|
|
@@ -1,54 +1,36 @@
|
|
|
1
|
-
from ase.build import bulk
|
|
2
1
|
from mat3ra.made.material import Material
|
|
3
2
|
from mat3ra.made.tools.build.utils import merge_materials
|
|
4
|
-
from mat3ra.made.tools.convert import from_ase
|
|
5
|
-
from mat3ra.made.tools.modify import filter_by_layers
|
|
6
3
|
from mat3ra.utils import assertion as assertion_utils
|
|
4
|
+
from unit.fixtures.cuts import (
|
|
5
|
+
CAVITY_MATERIAL_BASIS,
|
|
6
|
+
FULL_MATERIAL,
|
|
7
|
+
MERGED_CAVITY_SECTION_BASIS,
|
|
8
|
+
MERGED_SECTION_CAVITY_BASIS,
|
|
9
|
+
SECTION_MATERIAL_BASIS,
|
|
10
|
+
SECTION_MATERIAL_BASIS_EXTRA_ATOM,
|
|
11
|
+
)
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
cavity = filter_by_layers(material, central_atom_id=0, layer_thickness=1.0, invert_selection=True)
|
|
13
|
+
section = Material.create({**FULL_MATERIAL, **SECTION_MATERIAL_BASIS})
|
|
14
|
+
cavity = Material.create({**FULL_MATERIAL, **CAVITY_MATERIAL_BASIS})
|
|
15
|
+
section_with_extra_atom = Material.create({**FULL_MATERIAL, **SECTION_MATERIAL_BASIS_EXTRA_ATOM})
|
|
12
16
|
|
|
13
|
-
# Change 0th element
|
|
14
|
-
section.basis.elements.values[0] = "Ge"
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
expected_merged_material_basis = {
|
|
22
|
-
"elements": [{"id": 0, "value": "Ge"}, {"id": 1, "value": "Ni"}, {"id": 2, "value": "Ni"}, {"id": 4, "value": "S"}],
|
|
23
|
-
"coordinates": [
|
|
24
|
-
{"id": 0, "value": [0.0, 0.0, 0.0]},
|
|
25
|
-
{"id": 1, "value": [0.0, 0.5, 0.5]},
|
|
26
|
-
{"id": 2, "value": [0.5, 0.0, 0.5]},
|
|
27
|
-
{"id": 4, "value": [0.5, 0.5, 0.0]},
|
|
28
|
-
],
|
|
29
|
-
"labels": [],
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
expected_merged_material_reverse_basis = {
|
|
34
|
-
"elements": [
|
|
35
|
-
{"id": 1, "value": "Ni"},
|
|
36
|
-
{"id": 2, "value": "Ni"},
|
|
37
|
-
{"id": 0, "value": "Ge"},
|
|
38
|
-
{"id": 3, "value": "Ni"},
|
|
39
|
-
],
|
|
40
|
-
"coordinates": [
|
|
41
|
-
{"id": 1, "value": [0.0, 0.5, 0.5]},
|
|
42
|
-
{"id": 2, "value": [0.5, 0.0, 0.5]},
|
|
43
|
-
{"id": 0, "value": [0.0, 0.0, 0.0]},
|
|
44
|
-
{"id": 3, "value": [0.5, 0.5, 0.0]},
|
|
45
|
-
],
|
|
46
|
-
"labels": [],
|
|
47
|
-
}
|
|
18
|
+
def test_merge_materials():
|
|
19
|
+
merged_material = merge_materials([section, cavity])
|
|
20
|
+
merged_material_reverse = merge_materials([cavity, section])
|
|
21
|
+
assertion_utils.assert_deep_almost_equal(merged_material.basis, MERGED_CAVITY_SECTION_BASIS)
|
|
22
|
+
assertion_utils.assert_deep_almost_equal(merged_material_reverse.basis, MERGED_SECTION_CAVITY_BASIS)
|
|
48
23
|
|
|
49
24
|
|
|
50
|
-
def
|
|
25
|
+
def test_resolve_close_coordinates_basis():
|
|
51
26
|
merged_material = merge_materials([section, cavity])
|
|
52
27
|
merged_material_reverse = merge_materials([cavity, section])
|
|
53
|
-
assertion_utils.assert_deep_almost_equal(merged_material.basis,
|
|
54
|
-
assertion_utils.assert_deep_almost_equal(merged_material_reverse.basis,
|
|
28
|
+
assertion_utils.assert_deep_almost_equal(merged_material.basis, MERGED_CAVITY_SECTION_BASIS)
|
|
29
|
+
assertion_utils.assert_deep_almost_equal(merged_material_reverse.basis, MERGED_SECTION_CAVITY_BASIS)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_resolve_close_coordinates_basis_extra_atom():
|
|
33
|
+
merged_material = merge_materials([section_with_extra_atom, cavity])
|
|
34
|
+
merged_material_reverse = merge_materials([cavity, section_with_extra_atom])
|
|
35
|
+
assertion_utils.assert_deep_almost_equal(merged_material.basis, MERGED_CAVITY_SECTION_BASIS)
|
|
36
|
+
assertion_utils.assert_deep_almost_equal(merged_material_reverse.basis, MERGED_SECTION_CAVITY_BASIS)
|
|
@@ -195,7 +195,14 @@ def test_create_terrace():
|
|
|
195
195
|
number_of_added_layers=1,
|
|
196
196
|
)
|
|
197
197
|
new_slab = TerraceSlabDefectBuilder().get_material(configuration=config)
|
|
198
|
-
|
|
198
|
+
coordinate_macosx = [0.777786396, 0.5, 0.414655236]
|
|
199
|
+
coordinate_linux_and_emscripten = [0.627786404, 0.25, 0.439235145]
|
|
200
|
+
defect_coordinate = new_slab.basis.coordinates.values[42]
|
|
201
|
+
atol = 10 ** (-COORDINATE_TOLERANCE)
|
|
202
|
+
try:
|
|
203
|
+
assertion_utils.assert_deep_almost_equal(coordinate_macosx, defect_coordinate, atol=atol)
|
|
204
|
+
except AssertionError:
|
|
205
|
+
assertion_utils.assert_deep_almost_equal(coordinate_linux_and_emscripten, defect_coordinate, atol=atol)
|
|
199
206
|
|
|
200
207
|
|
|
201
208
|
def test_create_defect_pair():
|
|
@@ -91,4 +91,4 @@ def test_create_surface_grain_boundary():
|
|
|
91
91
|
]
|
|
92
92
|
|
|
93
93
|
assert len(gb) == 1
|
|
94
|
-
assertion_utils.assert_deep_almost_equal(expected_cell_vectors, gb[0].basis.cell.
|
|
94
|
+
assertion_utils.assert_deep_almost_equal(expected_cell_vectors, gb[0].basis.cell.vector_arrays)
|
|
@@ -85,6 +85,6 @@ def test_create_commensurate_supercell_twisted_interface():
|
|
|
85
85
|
assert len(interfaces) == 1
|
|
86
86
|
interface = interfaces[0]
|
|
87
87
|
expected_cell_vectors = [[10.754672133, 0.0, 0.0], [5.377336066500001, 9.313819276550575, 0.0], [0.0, 0.0, 20.0]]
|
|
88
|
-
assertion_utils.assert_deep_almost_equal(expected_cell_vectors, interface.basis.cell.
|
|
88
|
+
assertion_utils.assert_deep_almost_equal(expected_cell_vectors, interface.basis.cell.vector_arrays)
|
|
89
89
|
expected_angle = 13.174
|
|
90
90
|
assert interface.metadata["build"]["configuration"]["actual_twist_angle"] == expected_angle
|