@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mat3ra/made",
3
- "version": "2024.7.1-0",
3
+ "version": "2024.7.2-0",
4
4
  "description": "MAterials DEsign library",
5
5
  "scripts": {
6
6
  "lint": "eslint --cache src/js tests/js && prettier --write src/js tests/js",
@@ -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
- x_min: float = 0.0,
245
- y_min: float = 0.0,
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
- x_min (float): The minimum x-coordinate of the rectangle.
258
- y_min (float): The minimum y-coordinate of the rectangle.
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, [x_min, y_min, 0], [x_max, y_max, 1])
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 point is inside a cylinder.
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 point is inside the cylinder, False otherwise.
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 point is inside a box.
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 point is inside the box, False otherwise.
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 point's projection along a specified direction vector
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 point is within the layer thickness, False otherwise.
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())