@mat3ra/made 2026.6.15-0 → 2026.6.29-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/.pre-commit-config.yaml +1 -1
- package/.prettierignore +1 -0
- package/AGENTS.md +133 -0
- package/MANIFEST.in +1 -0
- package/data/constants.json +13 -0
- package/data/reciprocal_paths.json +111 -0
- package/dist/js/data/reciprocal_paths.json +111 -0
- package/dist/js/lattice/reciprocal/paths.js +8 -113
- package/dist/js/made.d.ts +1 -1
- package/dist/js/made.js +1 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/js/constants/molecule.ts +1 -1
- package/src/js/lattice/reciprocal/paths.ts +10 -118
- package/src/js/made.ts +1 -1
- package/src/py/mat3ra/made/data_helper.py +5 -0
- package/src/py/mat3ra/made/lattice.py +34 -0
- package/src/py/mat3ra/made/material.py +1 -2
- package/src/py/mat3ra/made/reciprocal/__init__.py +5 -0
- package/src/py/mat3ra/made/reciprocal/lattice_reciprocal.py +147 -0
- package/src/py/mat3ra/made/reciprocal/paths.py +30 -0
- package/src/py/mat3ra/made/reciprocal/symmetry_points.py +385 -0
- package/src/py/mat3ra/made/tools/analyze/fingerprint/__init__.py +1 -1
- package/src/py/mat3ra/made/tools/analyze/fingerprint/layers/layered_fingerprint_along_axis.py +2 -3
- package/src/py/mat3ra/made/tools/analyze/interface/__init__.py +1 -1
- package/src/py/mat3ra/made/tools/analyze/lattice/analyzer.py +7 -5
- package/src/py/mat3ra/made/tools/analyze/lattice_planes.py +10 -9
- package/src/py/mat3ra/made/tools/analyze/lattice_swap_analyzer.py +3 -3
- package/src/py/mat3ra/made/tools/build/compound_pristine_structures/two_dimensional/heterostructure/helpers.py +0 -1
- package/src/py/mat3ra/made/tools/build/compound_pristine_structures/two_dimensional/heterostructure/utils.py +10 -11
- package/src/py/mat3ra/made/tools/build_components/operations/core/combinations/stack/builder.py +26 -13
- package/src/py/mat3ra/made/tools/build_components/operations/core/modifications/perturb/helpers.py +2 -2
- package/src/py/mat3ra/made/tools/build_components/operations/core/modifications/strain/builder.py +1 -1
- package/src/py/mat3ra/made/tools/convert/__init__.py +8 -11
- package/src/py/mat3ra/made/tools/convert/utils.py +9 -5
- package/src/py/mat3ra/made/tools/operations/reusable/unary.py +2 -2
- package/tests/js/parsers/poscar.ts +9 -9
- package/tests/py/unit/fixtures/thrid_party/ase_atoms.py +0 -1
- package/tests/py/unit/test_data_files.py +68 -0
- package/tests/py/unit/test_lattice.py +3 -4
- package/tests/py/unit/test_lattice_type_extended.py +53 -0
- package/tests/py/unit/test_reciprocal_lattice.py +86 -0
- package/tests/py/unit/test_reciprocal_paths.py +64 -0
- package/tests/py/unit/test_symmetry_points.py +151 -0
- package/tests/py/unit/test_tools_analyze.py +7 -1
- package/tests/py/unit/test_tools_analyze_basis.py +1 -1
- package/tests/py/unit/test_tools_analyze_lattice.py +9 -4
- package/tests/py/unit/test_tools_analyze_swap.py +15 -9
- package/tests/py/unit/test_tools_build_defect/test_point_defect.py +24 -3
- package/tests/py/unit/test_tools_build_interface.py +1 -2
- package/tests/py/unit/test_tools_build_interface_zsl.py +72 -4
- package/tests/py/unit/test_tools_build_metadata.py +0 -1
- package/tests/py/unit/test_tools_calculate.py +1 -0
- package/tests/py/unit/test_tools_convert.py +1 -4
- package/tests/py/unit/utils.py +29 -0
- package/constants.json +0 -13
- package/src/py/mat3ra/made/constants.py +0 -11
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default k-point paths according to:
|
|
3
|
+
* [AFLOW](https://arxiv.org/abs/1004.2974) methodology.
|
|
4
|
+
* Paths are split in parts for clarity.
|
|
5
|
+
*
|
|
6
|
+
* Raw segment data is stored in data/reciprocal_paths.json (shared with Python).
|
|
7
|
+
*/
|
|
8
|
+
import points from "../../data/reciprocal_paths.json";
|
|
9
|
+
|
|
1
10
|
interface KPointStep {
|
|
2
11
|
point: string;
|
|
3
12
|
steps: number;
|
|
@@ -7,123 +16,6 @@ interface PathsType {
|
|
|
7
16
|
[key: string]: KPointStep[];
|
|
8
17
|
}
|
|
9
18
|
|
|
10
|
-
/**
|
|
11
|
-
* Default kpoing paths according to:
|
|
12
|
-
* [AFLOW](https://arxiv.org/abs/1004.2974) methodology.
|
|
13
|
-
* Paths are split in parts for clarity.
|
|
14
|
-
*/
|
|
15
|
-
const points: Record<string, string[][]> = {
|
|
16
|
-
CUB: [
|
|
17
|
-
["Г", "X", "M", "Г", "R", "X"],
|
|
18
|
-
["M", "R"],
|
|
19
|
-
],
|
|
20
|
-
BCC: [
|
|
21
|
-
["Г", "H", "N", "Г", "P", "H"],
|
|
22
|
-
["P", "N"],
|
|
23
|
-
],
|
|
24
|
-
FCC: [
|
|
25
|
-
["Г", "X", "W", "K", "Г", "L", "U", "W", "L"],
|
|
26
|
-
["U", "X"],
|
|
27
|
-
],
|
|
28
|
-
TET: [
|
|
29
|
-
["Г", "X", "M", "Г", "Z", "R", "A", "Z"],
|
|
30
|
-
["X", "R"],
|
|
31
|
-
["M", "A"],
|
|
32
|
-
],
|
|
33
|
-
"BCT-1": [
|
|
34
|
-
["Г", "X", "M", "Г", "Z", "P", "N", "Z1", "M"],
|
|
35
|
-
["X", "P"],
|
|
36
|
-
],
|
|
37
|
-
"BCT-2": [
|
|
38
|
-
["Г", "X", "Y", "∑", "Г", "Z", "∑1", "N", "P", "Y1", "Z"],
|
|
39
|
-
["X", "P"],
|
|
40
|
-
],
|
|
41
|
-
ORC: [
|
|
42
|
-
["Г", "X", "S", "Y", "Г", "Z", "U", "R", "T", "Z"],
|
|
43
|
-
["Y", "T"],
|
|
44
|
-
["U", "X"],
|
|
45
|
-
["S", "R"],
|
|
46
|
-
],
|
|
47
|
-
"ORCF-1": [
|
|
48
|
-
["Г", "Y", "T", "Z", "Г", "X", "A1", "Y"],
|
|
49
|
-
["T", "X1"],
|
|
50
|
-
["X", "A", "Z"],
|
|
51
|
-
["L", "Г"],
|
|
52
|
-
],
|
|
53
|
-
"ORCF-2": [
|
|
54
|
-
["Г", "Y", "C", "D", "X", "Г", "Z", "D1", "H", "C"],
|
|
55
|
-
["C1", "Z"],
|
|
56
|
-
["X", "H1"],
|
|
57
|
-
["H", "Y"],
|
|
58
|
-
["L", "Г"],
|
|
59
|
-
],
|
|
60
|
-
"ORCF-3": [
|
|
61
|
-
["Г", "Y", "T", "Z", "Г", "X", "A1", "Y"],
|
|
62
|
-
["X", "A", "Z"],
|
|
63
|
-
["L", "Г"],
|
|
64
|
-
],
|
|
65
|
-
ORCI: [
|
|
66
|
-
["Г", "X", "L", "T", "W", "R", "X1", "Z", "Г", "Y", "S", "W"],
|
|
67
|
-
["L1", "Y"],
|
|
68
|
-
["Y1", "Z"],
|
|
69
|
-
],
|
|
70
|
-
ORCC: [
|
|
71
|
-
["Г", "X", "S", "R", "A", "Z", "Г", "Y", "X1", "A1", "T", "Y"],
|
|
72
|
-
["Z", "T"],
|
|
73
|
-
],
|
|
74
|
-
HEX: [
|
|
75
|
-
["Г", "M", "K", "Г", "A", "L", "H", "A"],
|
|
76
|
-
["L", "M"],
|
|
77
|
-
["K", "H"],
|
|
78
|
-
],
|
|
79
|
-
"RHL-1": [
|
|
80
|
-
["Г", "L", "B1"],
|
|
81
|
-
["B", "Z", "Г", "X"],
|
|
82
|
-
["Q", "F", "P1", "Z"],
|
|
83
|
-
["L", "P"],
|
|
84
|
-
],
|
|
85
|
-
"RHL-2": [["Г", "P", "Z", "Q", "Г", "F", "P1", "Q1", "L", "Z"]],
|
|
86
|
-
MCL: [
|
|
87
|
-
["Г", "Y", "H", "C", "E", "M1", "A", "X", "H1"],
|
|
88
|
-
["M", "D", "Z"],
|
|
89
|
-
["Y", "D"],
|
|
90
|
-
],
|
|
91
|
-
"MCLC-1": [
|
|
92
|
-
["Г", "Y", "Г", "L", "I"],
|
|
93
|
-
["I1", "Z", "F1"],
|
|
94
|
-
["Y", "X1"],
|
|
95
|
-
["X", "Г", "N"],
|
|
96
|
-
["M", "Г"],
|
|
97
|
-
],
|
|
98
|
-
"MCLC-2": [
|
|
99
|
-
["Г", "Y", "F", "L", "I"],
|
|
100
|
-
["I1", "Z", "F1"],
|
|
101
|
-
["N", "Г", "M"],
|
|
102
|
-
],
|
|
103
|
-
"MCLC-3": [
|
|
104
|
-
["Г", "Y", "F", "H", "Z", "I", "F1"],
|
|
105
|
-
["H1", "Y1", "X", "Г", "N"],
|
|
106
|
-
["M", "Г"],
|
|
107
|
-
],
|
|
108
|
-
"MCLC-4": [
|
|
109
|
-
["Г", "Y", "F", "H", "Z", "I"],
|
|
110
|
-
["H1", "Y1", "X", "Г", "N"],
|
|
111
|
-
["M", "Г"],
|
|
112
|
-
],
|
|
113
|
-
"MCLC-5": [
|
|
114
|
-
["Г", "Y", "F", "L", "I"],
|
|
115
|
-
["I1", "Z", "H", "F1"],
|
|
116
|
-
["H1", "Y1", "X", "Г", "N"],
|
|
117
|
-
["M", "Г"],
|
|
118
|
-
],
|
|
119
|
-
TRI: [
|
|
120
|
-
["X", "Г", "Y"],
|
|
121
|
-
["L", "Г", "Z"],
|
|
122
|
-
["N", "Г", "M"],
|
|
123
|
-
["R", "Г"],
|
|
124
|
-
],
|
|
125
|
-
};
|
|
126
|
-
|
|
127
19
|
// Export a processed version of the paths
|
|
128
20
|
export const paths: PathsType = (() => {
|
|
129
21
|
const result: PathsType = {};
|
|
@@ -132,7 +24,7 @@ export const paths: PathsType = (() => {
|
|
|
132
24
|
// Flatten arrays of path segments into a single array
|
|
133
25
|
const flattenedPath: string[] = pathSegments.reduce(
|
|
134
26
|
(acc, segment) => [...acc, ...segment],
|
|
135
|
-
[],
|
|
27
|
+
[] as string[],
|
|
136
28
|
);
|
|
137
29
|
|
|
138
30
|
// Convert to KPointStep array
|
package/src/js/made.ts
CHANGED
|
@@ -3,9 +3,9 @@ import { Cell } from "./cell/cell";
|
|
|
3
3
|
import { ATOMIC_COORD_UNITS, coefficients, tolerance, units } from "./constants";
|
|
4
4
|
import { AtomicConstraints } from "./constraints/constraints";
|
|
5
5
|
import {
|
|
6
|
-
Lattice,
|
|
7
6
|
defaultNonPeriodicMinimumLatticeSize,
|
|
8
7
|
diatomicLatticePaddingFactor,
|
|
8
|
+
Lattice,
|
|
9
9
|
molecularLatticePaddingFactor,
|
|
10
10
|
} from "./lattice/lattice";
|
|
11
11
|
import { DEFAULT_LATTICE_UNITS, LATTICE_TYPE_CONFIGS } from "./lattice/lattice_types";
|
|
@@ -142,6 +142,40 @@ class Lattice(RoundNumericValuesMixin, LatticeSchemaVectorless, InMemoryEntityPy
|
|
|
142
142
|
max_norm = max(norms)
|
|
143
143
|
return [round(float(value / max_norm), 3) for value in norms]
|
|
144
144
|
|
|
145
|
+
@property
|
|
146
|
+
def type_extended(self) -> str:
|
|
147
|
+
"""Get the extended lattice type classification.
|
|
148
|
+
|
|
149
|
+
For lattice types with subtypes (BCT, ORCF, RHL, MCLC, TRI),
|
|
150
|
+
returns the specific subtype based on lattice parameters.
|
|
151
|
+
For other types, returns the base type string.
|
|
152
|
+
"""
|
|
153
|
+
a, b, c = self.a, self.b, self.c
|
|
154
|
+
alpha, beta, gamma = self.alpha, self.beta, self.gamma
|
|
155
|
+
cos_alpha = math.cos(math.radians(alpha))
|
|
156
|
+
lattice_type = self.type.value if hasattr(self.type, "value") else str(self.type)
|
|
157
|
+
|
|
158
|
+
if lattice_type == "BCT":
|
|
159
|
+
return "BCT-1" if c < a else "BCT-2"
|
|
160
|
+
elif lattice_type == "ORCF":
|
|
161
|
+
if 1 / (a * a) >= 1 / (b * b) + 1 / (c * c):
|
|
162
|
+
return "ORCF-1"
|
|
163
|
+
return "ORCF-2"
|
|
164
|
+
elif lattice_type == "RHL":
|
|
165
|
+
return "RHL-1" if cos_alpha > 0 else "RHL-2"
|
|
166
|
+
elif lattice_type == "MCLC":
|
|
167
|
+
if gamma >= 90:
|
|
168
|
+
return "MCLC-1"
|
|
169
|
+
if (b / c) * cos_alpha + (b * b / (a * a)) * (1 - cos_alpha * cos_alpha) <= 1:
|
|
170
|
+
return "MCLC-3"
|
|
171
|
+
return "MCLC-5"
|
|
172
|
+
elif lattice_type == "TRI":
|
|
173
|
+
if alpha > 90 and beta > 90 and gamma >= 90:
|
|
174
|
+
return "TRI_1a"
|
|
175
|
+
return "TRI_1b"
|
|
176
|
+
else:
|
|
177
|
+
return lattice_type
|
|
178
|
+
|
|
145
179
|
def get_hash_string(self, is_scaled: bool = False) -> str:
|
|
146
180
|
"""Mirrors JS Lattice.getHashString(isScaled). Rounds to HASH_TOLERANCE decimal places."""
|
|
147
181
|
scale = self.a if is_scaled else 1
|
|
@@ -55,7 +55,7 @@ defaultMaterialConfig = {
|
|
|
55
55
|
# TODO: replace `-Pydantic` with actual class in the next PR
|
|
56
56
|
class Material(MaterialSchema, HasDescriptionHasMetadataNamedDefaultableInMemoryEntityPydantic):
|
|
57
57
|
model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True)
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
__default_config__ = defaultMaterialConfig
|
|
60
60
|
__schema__ = MaterialSchema
|
|
61
61
|
|
|
@@ -132,4 +132,3 @@ class Material(MaterialSchema, HasDescriptionHasMetadataNamedDefaultableInMemory
|
|
|
132
132
|
@property
|
|
133
133
|
def scaled_hash(self) -> str:
|
|
134
134
|
return self.calculate_hash(is_scaled=True)
|
|
135
|
-
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Reciprocal lattice class with k-point path and symmetry point utilities.
|
|
3
|
+
|
|
4
|
+
Extends the base Lattice class with reciprocal-space functionality
|
|
5
|
+
for Brillouin zone analysis.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from ..lattice import Lattice
|
|
13
|
+
from .paths import RECIPROCAL_PATHS
|
|
14
|
+
from .symmetry_points import get_symmetry_points
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ReciprocalLattice(Lattice):
|
|
18
|
+
"""Lattice with reciprocal-space utilities.
|
|
19
|
+
|
|
20
|
+
Provides methods for working with reciprocal lattice vectors,
|
|
21
|
+
high-symmetry points, k-point paths, and k-grid dimensions.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def cartesian_coordinates(self, point: List[float]) -> List[float]:
|
|
25
|
+
"""Convert a point in crystal (fractional) coordinates to Cartesian coordinates.
|
|
26
|
+
|
|
27
|
+
The Cartesian coordinates are in units of 2π/a.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
point: A 3D point in fractional reciprocal-space coordinates.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
The point in Cartesian coordinates.
|
|
34
|
+
"""
|
|
35
|
+
return np.dot(point, self.reciprocal_vectors).tolist()
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def symmetry_points(self) -> List[dict]:
|
|
39
|
+
"""Get the list of high-symmetry points for the current lattice.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
List of dicts with 'point' (str) and 'coordinates' ([float, float, float]).
|
|
43
|
+
"""
|
|
44
|
+
return get_symmetry_points(self)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def default_kpoint_path(self) -> List[dict]:
|
|
48
|
+
"""Get the default path in reciprocal space for the current lattice.
|
|
49
|
+
|
|
50
|
+
Looks up the path by extended type first (e.g., 'BCT-1'), falling back
|
|
51
|
+
to the base type (e.g., 'BCT').
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
List of dicts with 'point' (str) and 'steps' (int).
|
|
55
|
+
"""
|
|
56
|
+
path = RECIPROCAL_PATHS.get(self.type_extended)
|
|
57
|
+
if path is None:
|
|
58
|
+
lattice_type = self.type.value if hasattr(self.type, "value") else str(self.type)
|
|
59
|
+
path = RECIPROCAL_PATHS.get(lattice_type, [])
|
|
60
|
+
return path
|
|
61
|
+
|
|
62
|
+
def extract_kpoint_path(self, data_points: Optional[List[List[float]]] = None) -> List[dict]:
|
|
63
|
+
"""Find and label high-symmetry points in a list of raw k-point coordinates.
|
|
64
|
+
|
|
65
|
+
Scans through the provided data points and identifies any that match
|
|
66
|
+
known symmetry points (within tolerance).
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
data_points: List of 3D k-point coordinates.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
List of dicts with 'point' (str), 'steps' (int), and
|
|
73
|
+
'coordinates' ([float, float, float]) for each matched symmetry point.
|
|
74
|
+
"""
|
|
75
|
+
if data_points is None:
|
|
76
|
+
data_points = []
|
|
77
|
+
|
|
78
|
+
kpoint_path: List[dict] = []
|
|
79
|
+
symm_points = self.symmetry_points
|
|
80
|
+
|
|
81
|
+
for index, point in enumerate(data_points):
|
|
82
|
+
for sp in symm_points:
|
|
83
|
+
if np.allclose(sp["coordinates"], point, atol=1e-4):
|
|
84
|
+
kpoint_path.append(
|
|
85
|
+
{
|
|
86
|
+
"point": sp["point"],
|
|
87
|
+
"steps": index,
|
|
88
|
+
"coordinates": sp["coordinates"],
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
break
|
|
92
|
+
|
|
93
|
+
return kpoint_path
|
|
94
|
+
|
|
95
|
+
def calculate_dimension(self, n_points: int, index: int) -> int:
|
|
96
|
+
"""Calculate k-grid dimension for one reciprocal direction.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
n_points: Total number of k-points.
|
|
100
|
+
index: Index of the reciprocal vector direction (0, 1, or 2).
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Grid dimension in the specified direction.
|
|
104
|
+
"""
|
|
105
|
+
norms = self.reciprocal_vector_norms
|
|
106
|
+
others = [i for i in range(3) if i != index]
|
|
107
|
+
j, k = others[0], others[1]
|
|
108
|
+
n = np.cbrt((n_points * norms[index] ** 2) / (norms[j] * norms[k]))
|
|
109
|
+
return max(1, int(np.ceil(n)))
|
|
110
|
+
|
|
111
|
+
def get_dimensions_from_points_count(self, n_kpoints: int) -> List[int]:
|
|
112
|
+
"""Calculate 3D grid dimensions from total number of k-points.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
n_kpoints: Total number of k-points.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
List of three grid dimensions [n1, n2, n3].
|
|
119
|
+
"""
|
|
120
|
+
return [self.calculate_dimension(n_kpoints, i) for i in range(3)]
|
|
121
|
+
|
|
122
|
+
def get_dimensions_from_spacing(self, spacing: float) -> List[int]:
|
|
123
|
+
"""Calculate grid dimensions from k-point spacing.
|
|
124
|
+
|
|
125
|
+
The spacing is in Cartesian (2π/a) units.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
spacing: Maximum spacing between k-points.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
List of three grid dimensions [n1, n2, n3].
|
|
132
|
+
"""
|
|
133
|
+
norms = self.reciprocal_vector_norms
|
|
134
|
+
return [max(1, int(np.ceil(round(norm / spacing, 4)))) for norm in norms]
|
|
135
|
+
|
|
136
|
+
def get_spacing_from_dimensions(self, dimensions: List[int]) -> float:
|
|
137
|
+
"""Calculate average k-point spacing from grid dimensions.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
dimensions: List of three grid dimensions [n1, n2, n3].
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Average spacing in Cartesian (2π/a) units.
|
|
144
|
+
"""
|
|
145
|
+
norms = self.reciprocal_vector_norms
|
|
146
|
+
spacings = [norms[i] / max(1, dimensions[i]) for i in range(3)]
|
|
147
|
+
return float(np.mean(spacings))
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Default k-point paths according to AFLOW methodology.
|
|
3
|
+
|
|
4
|
+
Reference: https://arxiv.org/abs/1004.2974
|
|
5
|
+
Paths are split in parts for clarity.
|
|
6
|
+
|
|
7
|
+
Raw segment data is stored in reciprocal_paths.json (shared with JS/TS).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
from typing import Dict, List
|
|
12
|
+
|
|
13
|
+
from mat3ra.made.data_helper import DATA_DIR
|
|
14
|
+
|
|
15
|
+
_JSON_FILE = DATA_DIR / "reciprocal_paths.json"
|
|
16
|
+
with open(_JSON_FILE) as f:
|
|
17
|
+
_POINTS: Dict[str, List[List[str]]] = json.load(f)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _build_reciprocal_paths() -> Dict[str, List[dict]]:
|
|
21
|
+
"""Flatten path segments and assign default step count to each point."""
|
|
22
|
+
result: Dict[str, List[dict]] = {}
|
|
23
|
+
for key, path_segments in _POINTS.items():
|
|
24
|
+
flattened = [point for segment in path_segments for point in segment]
|
|
25
|
+
# TODO: calculate number of steps based on distance in k-space
|
|
26
|
+
result[key] = [{"point": point, "steps": 10} for point in flattened]
|
|
27
|
+
return result
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
RECIPROCAL_PATHS: Dict[str, List[dict]] = _build_reciprocal_paths()
|