@mat3ra/made 2026.5.7-0 → 2026.5.21-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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mat3ra/made",
3
- "version": "2026.5.7-0",
3
+ "version": "2026.5.21-1",
4
4
  "description": "MAterials DEsign library",
5
5
  "scripts": {
6
6
  "lint": "eslint --cache src/js tests/js && prettier --write src/js tests/js",
@@ -4,6 +4,7 @@ from .simple import InterfaceAnalyzer
4
4
  from .twisted_nanoribbons import TwistedNanoribbonsInterfaceAnalyzer
5
5
  from .utils.holders import MatchedSubstrateFilmConfigurationHolder
6
6
  from .zsl import ZSLInterfaceAnalyzer, ZSLMatchHolder
7
+ from .utils import calculate_interfacial_distance_from_rdf
7
8
 
8
9
  __all__ = [
9
10
  "InterfaceAnalyzer",
@@ -15,4 +16,5 @@ __all__ = [
15
16
  "GrainBoundaryPlanarMatchHolder",
16
17
  "TwistedNanoribbonsInterfaceAnalyzer",
17
18
  "MatchedSubstrateFilmConfigurationHolder",
19
+ "calculate_interfacial_distance_from_rdf",
18
20
  ]
@@ -1,5 +1,77 @@
1
+ from typing import Union
2
+
3
+ from mat3ra.made.material import Material
4
+
5
+ from ....build.pristine_structures.two_dimensional.slab import SlabConfiguration
6
+ from ....build_components.entities.reusable.three_dimensional.supercell.helpers import create_supercell
7
+ from ...rdf import RadialDistributionFunction
1
8
  from .holders import MatchedSubstrateFilmConfigurationHolder
2
9
 
10
+
11
+ def calculate_interfacial_distance_from_rdf(
12
+ substrate_material: Union[Material, dict, "SlabConfiguration"],
13
+ film_material: Union[Material, dict, "SlabConfiguration"],
14
+ rdf_cutoff: float = 10.0,
15
+ rdf_bin_size: float = 0.1,
16
+ supercell_size: tuple = (3, 3, 3),
17
+ ) -> float:
18
+ """
19
+ Calculate interfacial distance based on RDF analysis of bulk materials.
20
+
21
+ Creates temporary supercells of substrate and film bulk materials,
22
+ calculates their RDFs to find the first peak (nearest neighbor distance),
23
+ and returns the average of these distances as the initial guess for interfacial distance.
24
+
25
+ Args:
26
+ substrate_material: Material, dict, or SlabConfiguration for the substrate
27
+ film_material: Material, dict, or SlabConfiguration for the film
28
+ rdf_cutoff: Maximum distance for RDF calculation in Angstroms
29
+ rdf_bin_size: Bin size for RDF histogram in Angstroms
30
+ supercell_size: Size of supercell for RDF analysis (default: 3x3x3)
31
+
32
+ Returns:
33
+ float: Calculated interfacial distance in Angstroms
34
+ """
35
+
36
+ if isinstance(substrate_material, SlabConfiguration):
37
+ substrate_bulk = substrate_material.atomic_layers.crystal
38
+ elif isinstance(substrate_material, dict):
39
+ substrate_bulk = Material.create(substrate_material)
40
+ else:
41
+ substrate_bulk = substrate_material
42
+
43
+ if isinstance(film_material, SlabConfiguration):
44
+ film_bulk = film_material.atomic_layers.crystal
45
+ elif isinstance(film_material, dict):
46
+ film_bulk = Material.create(film_material)
47
+ else:
48
+ film_bulk = film_material
49
+
50
+ substrate_supercell = create_supercell(material=substrate_bulk, scaling_factor=list(supercell_size))
51
+
52
+ film_supercell = create_supercell(material=film_bulk, scaling_factor=list(supercell_size))
53
+
54
+ substrate_rdf = RadialDistributionFunction.from_material(
55
+ substrate_supercell,
56
+ cutoff=rdf_cutoff,
57
+ bin_size=rdf_bin_size,
58
+ )
59
+
60
+ film_rdf = RadialDistributionFunction.from_material(
61
+ film_supercell,
62
+ cutoff=rdf_cutoff,
63
+ bin_size=rdf_bin_size,
64
+ )
65
+
66
+ substrate_first_peak = substrate_rdf.first_peak_distance
67
+ film_first_peak = film_rdf.first_peak_distance
68
+
69
+ interfacial_distance = (substrate_first_peak + film_first_peak) / 2.0
70
+
71
+ return interfacial_distance
72
+
73
+
3
74
  __all__ = [
4
75
  "MatchedSubstrateFilmConfigurationHolder",
76
+ "calculate_interfacial_distance_from_rdf",
5
77
  ]
@@ -3,8 +3,10 @@ from typing import Final
3
3
 
4
4
  import numpy as np
5
5
  import pytest
6
+ from mat3ra.made.material import Material
6
7
  from mat3ra.made.tools.analyze.interface import InterfaceAnalyzer
7
8
  from mat3ra.made.tools.analyze.interface.commensurate import CommensurateLatticeInterfaceAnalyzer
9
+ from mat3ra.made.tools.analyze.interface.utils import calculate_interfacial_distance_from_rdf
8
10
  from mat3ra.made.tools.build.pristine_structures.two_dimensional.slab import SlabConfiguration
9
11
  from unit.fixtures.bulk import BULK_GRAPHENE, BULK_Ge_CONVENTIONAL, BULK_Si_CONVENTIONAL
10
12
 
@@ -195,3 +197,27 @@ def test_optimal_supercell_functions(substrate, film, expected_n, expected_m):
195
197
 
196
198
  assert optimal_n == expected_n
197
199
  assert optimal_m == expected_m
200
+
201
+
202
+ @pytest.mark.parametrize(
203
+ "substrate_config, film_config, expected_distance_range",
204
+ [
205
+ (BULK_Si_CONVENTIONAL, BULK_Si_CONVENTIONAL, (3.8, 3.9)),
206
+ (BULK_Si_CONVENTIONAL, BULK_Ge_CONVENTIONAL, (3.1, 3.2)),
207
+ ],
208
+ )
209
+ def test_calculate_interfacial_distance_from_rdf(substrate_config, film_config, expected_distance_range):
210
+ """Test RDF-based interfacial distance calculation with different material types."""
211
+ substrate_material = Material.create(substrate_config)
212
+ film_material = Material.create(film_config)
213
+
214
+ distance = calculate_interfacial_distance_from_rdf(
215
+ substrate_material=substrate_material,
216
+ film_material=film_material,
217
+ rdf_cutoff=10.0,
218
+ rdf_bin_size=0.1,
219
+ supercell_size=(3, 3, 3),
220
+ )
221
+
222
+ assert isinstance(distance, float)
223
+ assert expected_distance_range[0] <= distance <= expected_distance_range[1]