@mat3ra/made 2024.7.1-0 → 2024.7.2-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
|
@@ -9,6 +9,7 @@ from .third_party import PymatgenSpacegroupAnalyzer, PymatgenStructure
|
|
|
9
9
|
from .utils import (
|
|
10
10
|
is_coordinate_in_box,
|
|
11
11
|
is_coordinate_in_cylinder,
|
|
12
|
+
is_coordinate_in_triangular_prism,
|
|
12
13
|
is_coordinate_within_layer,
|
|
13
14
|
translate_to_bottom_pymatgen_structure,
|
|
14
15
|
)
|
|
@@ -241,10 +242,8 @@ def filter_by_cylinder(
|
|
|
241
242
|
|
|
242
243
|
def filter_by_rectangle_projection(
|
|
243
244
|
material: Material,
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
x_max: float = 1.0,
|
|
247
|
-
y_max: float = 1.0,
|
|
245
|
+
min_coordinate: List[float] = [0, 0],
|
|
246
|
+
max_coordinate: List[float] = [1, 1],
|
|
248
247
|
use_cartesian_coordinates: bool = False,
|
|
249
248
|
invert_selection: bool = False,
|
|
250
249
|
) -> Material:
|
|
@@ -252,21 +251,20 @@ def filter_by_rectangle_projection(
|
|
|
252
251
|
Get material with atoms that are within or outside an XY rectangle projection.
|
|
253
252
|
|
|
254
253
|
Args:
|
|
255
|
-
|
|
256
254
|
material (Material): The material object to filter.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
x_max (float): The maximum x-coordinate of the rectangle.
|
|
260
|
-
y_max (float): The maximum y-coordinate of the rectangle.
|
|
255
|
+
min_coordinate (List[float]): The minimum coordinate of the rectangle.
|
|
256
|
+
max_coordinate (List[float]): The maximum coordinate of the rectangle.
|
|
261
257
|
use_cartesian_coordinates (bool): Whether to use cartesian coordinates
|
|
262
258
|
invert_selection (bool): Whether to invert the selection.
|
|
263
259
|
|
|
264
260
|
Returns:
|
|
265
261
|
Material: The filtered material object.
|
|
266
262
|
"""
|
|
263
|
+
min_coordinate = min_coordinate[:2] + [0]
|
|
264
|
+
max_coordinate = max_coordinate[:2] + [1]
|
|
267
265
|
|
|
268
266
|
def condition(coordinate):
|
|
269
|
-
return is_coordinate_in_box(coordinate,
|
|
267
|
+
return is_coordinate_in_box(coordinate, min_coordinate, max_coordinate)
|
|
270
268
|
|
|
271
269
|
return filter_by_condition_on_coordinates(
|
|
272
270
|
material, condition, use_cartesian_coordinates=use_cartesian_coordinates, invert_selection=invert_selection
|
|
@@ -290,3 +288,38 @@ def filter_by_box(
|
|
|
290
288
|
return filter_by_condition_on_coordinates(
|
|
291
289
|
material, condition, use_cartesian_coordinates=use_cartesian_coordinates, invert_selection=invert_selection
|
|
292
290
|
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def filter_by_triangle_projection(
|
|
294
|
+
material: Material,
|
|
295
|
+
coordinate_1: List[float] = [0, 0],
|
|
296
|
+
coordinate_2: List[float] = [0, 1],
|
|
297
|
+
coordinate_3: List[float] = [1, 0],
|
|
298
|
+
min_z: float = 0,
|
|
299
|
+
max_z: float = 1,
|
|
300
|
+
use_cartesian_coordinates: bool = False,
|
|
301
|
+
invert_selection: bool = False,
|
|
302
|
+
) -> Material:
|
|
303
|
+
"""
|
|
304
|
+
Get material with atoms that are within or outside a prism formed by triangle projection.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
material (Material): The material object to filter.
|
|
308
|
+
coordinate_1 (List[float]): The coordinate of the first vertex.
|
|
309
|
+
coordinate_2 (List[float]): The coordinate of the second vertex.
|
|
310
|
+
coordinate_3 (List[float]): The coordinate of the third vertex.
|
|
311
|
+
min_z (float): Lower limit of z-coordinate.
|
|
312
|
+
max_z (float): Upper limit of z-coordinate.
|
|
313
|
+
use_cartesian_coordinates (bool): Whether to use cartesian coordinates
|
|
314
|
+
invert_selection (bool): Whether to invert the selection.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Material: The filtered material object.
|
|
318
|
+
"""
|
|
319
|
+
|
|
320
|
+
def condition(coordinate):
|
|
321
|
+
return is_coordinate_in_triangular_prism(coordinate, coordinate_1, coordinate_2, coordinate_3, min_z, max_z)
|
|
322
|
+
|
|
323
|
+
return filter_by_condition_on_coordinates(
|
|
324
|
+
material, condition, use_cartesian_coordinates=use_cartesian_coordinates, invert_selection=invert_selection
|
|
325
|
+
)
|
|
@@ -105,7 +105,7 @@ def is_coordinate_in_cylinder(
|
|
|
105
105
|
coordinate: List[float], center_position: List[float], radius: float = 0.25, min_z: float = 0, max_z: float = 1
|
|
106
106
|
) -> bool:
|
|
107
107
|
"""
|
|
108
|
-
Check if a
|
|
108
|
+
Check if a coordinate is inside a cylinder.
|
|
109
109
|
Args:
|
|
110
110
|
coordinate (List[float]): The coordinate to check.
|
|
111
111
|
center_position (List[float]): The coordinates of the center position.
|
|
@@ -114,7 +114,7 @@ def is_coordinate_in_cylinder(
|
|
|
114
114
|
radius (float): The radius of the cylinder.
|
|
115
115
|
|
|
116
116
|
Returns:
|
|
117
|
-
bool: True if the
|
|
117
|
+
bool: True if the coordinate is inside the cylinder, False otherwise.
|
|
118
118
|
"""
|
|
119
119
|
return (coordinate[0] - center_position[0]) ** 2 + (coordinate[1] - center_position[1]) ** 2 <= radius**2 and (
|
|
120
120
|
min_z <= coordinate[2] <= max_z
|
|
@@ -125,13 +125,13 @@ def is_coordinate_in_box(
|
|
|
125
125
|
coordinate: List[float], min_coordinate: List[float] = [0, 0, 0], max_coordinate: List[float] = [1, 1, 1]
|
|
126
126
|
) -> bool:
|
|
127
127
|
"""
|
|
128
|
-
Check if a
|
|
128
|
+
Check if a coordinate is inside a box.
|
|
129
129
|
Args:
|
|
130
130
|
coordinate (List[float]): The coordinate to check.
|
|
131
131
|
min_coordinate (List[float]): The minimum coordinate of the box.
|
|
132
132
|
max_coordinate (List[float]): The maximum coordinate of the box.
|
|
133
133
|
Returns:
|
|
134
|
-
bool: True if the
|
|
134
|
+
bool: True if the coordinate is inside the box, False otherwise.
|
|
135
135
|
"""
|
|
136
136
|
x_min, y_min, z_min = min_coordinate
|
|
137
137
|
x_max, y_max, z_max = max_coordinate
|
|
@@ -142,7 +142,7 @@ def is_coordinate_within_layer(
|
|
|
142
142
|
coordinate: List[float], center_position: List[float], direction_vector: List[float], layer_thickness: float
|
|
143
143
|
) -> bool:
|
|
144
144
|
"""
|
|
145
|
-
Checks if a
|
|
145
|
+
Checks if a coordinate's projection along a specified direction vector
|
|
146
146
|
is within a certain layer thickness centered around a given position.
|
|
147
147
|
|
|
148
148
|
Args:
|
|
@@ -152,7 +152,7 @@ def is_coordinate_within_layer(
|
|
|
152
152
|
layer_thickness (float): The thickness of the layer along the direction vector.
|
|
153
153
|
|
|
154
154
|
Returns:
|
|
155
|
-
bool: True if the
|
|
155
|
+
bool: True if the coordinate is within the layer thickness, False otherwise.
|
|
156
156
|
"""
|
|
157
157
|
direction_norm = np.array(direction_vector) / np.linalg.norm(direction_vector)
|
|
158
158
|
central_projection = np.dot(center_position, direction_norm)
|
|
@@ -162,3 +162,54 @@ def is_coordinate_within_layer(
|
|
|
162
162
|
upper_bound = central_projection + layer_thickness_frac / 2
|
|
163
163
|
|
|
164
164
|
return lower_bound <= np.dot(coordinate, direction_norm) <= upper_bound
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def is_coordinate_in_triangular_prism(
|
|
168
|
+
coordinate: List[float],
|
|
169
|
+
coordinate_1: List[float],
|
|
170
|
+
coordinate_2: List[float],
|
|
171
|
+
coordinate_3: List[float],
|
|
172
|
+
min_z: float = 0,
|
|
173
|
+
max_z: float = 1,
|
|
174
|
+
) -> bool:
|
|
175
|
+
"""
|
|
176
|
+
Check if a coordinate is inside a triangular prism.
|
|
177
|
+
Args:
|
|
178
|
+
coordinate (List[float]): The coordinate to check.
|
|
179
|
+
coordinate_1 (List[float]): The first coordinate of the triangle.
|
|
180
|
+
coordinate_2 (List[float]): The second coordinate of the triangle.
|
|
181
|
+
coordinate_3 (List[float]): The third coordinate of the triangle.
|
|
182
|
+
min_z (float): Lower limit of z-coordinate.
|
|
183
|
+
max_z (float): Upper limit of z-coordinate.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
bool: True if the coordinate is inside the triangular prism, False otherwise.
|
|
187
|
+
"""
|
|
188
|
+
# convert to 3D coordinates at the origin XY plane
|
|
189
|
+
coordinate_1.extend([0] * (3 - len(coordinate_1)))
|
|
190
|
+
coordinate_2.extend([0] * (3 - len(coordinate_2)))
|
|
191
|
+
coordinate_3.extend([0] * (3 - len(coordinate_3)))
|
|
192
|
+
|
|
193
|
+
coordinate = np.array(coordinate)
|
|
194
|
+
v1 = np.array(coordinate_1)
|
|
195
|
+
v2 = np.array(coordinate_2)
|
|
196
|
+
v3 = np.array(coordinate_3)
|
|
197
|
+
|
|
198
|
+
v2_v1 = v2 - v1
|
|
199
|
+
v3_v1 = v3 - v1
|
|
200
|
+
coordinate_v1 = coordinate - v1
|
|
201
|
+
|
|
202
|
+
# Compute dot products for the barycentric coordinates
|
|
203
|
+
d00 = np.dot(v2_v1, v2_v1)
|
|
204
|
+
d01 = np.dot(v2_v1, v3_v1)
|
|
205
|
+
d11 = np.dot(v3_v1, v3_v1)
|
|
206
|
+
d20 = np.dot(coordinate_v1, v2_v1)
|
|
207
|
+
d21 = np.dot(coordinate_v1, v3_v1)
|
|
208
|
+
|
|
209
|
+
# Calculate barycentric coordinates
|
|
210
|
+
denom = d00 * d11 - d01 * d01
|
|
211
|
+
v = (d11 * d20 - d01 * d21) / denom
|
|
212
|
+
w = (d00 * d21 - d01 * d20) / denom
|
|
213
|
+
u = 1.0 - v - w
|
|
214
|
+
|
|
215
|
+
return (u >= 0) and (v >= 0) and (w >= 0) and (u + v + w <= 1) and (min_z <= coordinate[2] <= max_z)
|
|
@@ -7,6 +7,7 @@ from mat3ra.made.tools.modify import (
|
|
|
7
7
|
filter_by_layers,
|
|
8
8
|
filter_by_rectangle_projection,
|
|
9
9
|
filter_by_sphere,
|
|
10
|
+
filter_by_triangle_projection,
|
|
10
11
|
)
|
|
11
12
|
from mat3ra.utils import assertion as assertion_utils
|
|
12
13
|
|
|
@@ -126,3 +127,12 @@ def test_filter_by_rectangle_projection():
|
|
|
126
127
|
# Default will contain all the atoms
|
|
127
128
|
section = filter_by_rectangle_projection(material)
|
|
128
129
|
assertion_utils.assert_deep_almost_equal(material.basis.to_json(), section.basis.to_json())
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_filter_by_triangle_projection():
|
|
133
|
+
# Small prism in the middle of the cell containing the central atom will be removed -- the same as with sphere
|
|
134
|
+
material = Material(SI_CONVENTIONAL_CELL)
|
|
135
|
+
section = filter_by_triangle_projection(material, [0.4, 0.4], [0.4, 0.5], [0.5, 0.5])
|
|
136
|
+
cavity = filter_by_triangle_projection(material, [0.4, 0.4], [0.4, 0.5], [0.5, 0.5], invert_selection=True)
|
|
137
|
+
assertion_utils.assert_deep_almost_equal(expected_basis_sphere_cluster, section.basis.to_json())
|
|
138
|
+
assertion_utils.assert_deep_almost_equal(expected_basis_sphere_cavity, cavity.basis.to_json())
|