@mat3ra/made 2024.5.15-1 → 2024.6.4-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/dist/js/cell/cell.d.ts +3 -2
- package/dist/js/lattice/lattice.d.ts +1 -1
- package/dist/js/material.d.ts +3 -8
- package/dist/js/math.d.ts +2 -2
- package/dist/js/tools/material.js +3 -6
- package/dist/js/types.d.ts +1 -1
- package/package.json +4 -6
- package/src/js/cell/cell.ts +3 -3
- package/src/js/material.ts +1 -1
- package/src/js/{math.js → math.ts} +2 -2
- package/src/js/tools/material.js +1 -1
- package/src/js/types.ts +1 -1
- package/src/py/mat3ra/made/tools/analyze.py +14 -0
- package/src/py/mat3ra/made/tools/build/__init__.py +98 -77
- package/src/py/mat3ra/made/tools/build/interface/__init__.py +26 -0
- package/src/py/mat3ra/made/tools/build/interface/builders.py +185 -0
- package/src/py/mat3ra/made/tools/build/interface/configuration.py +18 -0
- package/src/py/mat3ra/made/tools/build/interface/enums.py +7 -0
- package/src/py/mat3ra/made/tools/build/interface/termination_pair.py +59 -0
- package/src/py/mat3ra/made/tools/build/interface/utils.py +36 -0
- package/src/py/mat3ra/made/tools/build/mixins.py +13 -0
- package/src/py/mat3ra/made/tools/build/slab/__init__.py +15 -0
- package/src/py/mat3ra/made/tools/build/slab/builders.py +75 -0
- package/src/py/mat3ra/made/tools/build/slab/configuration.py +56 -0
- package/src/py/mat3ra/made/tools/build/slab/termination.py +27 -0
- package/src/py/mat3ra/made/tools/build/supercell.py +25 -0
- package/src/py/mat3ra/made/tools/convert.py +9 -2
- package/src/py/mat3ra/made/tools/utils.py +17 -0
- package/tests/py/unit/fixtures.py +115 -10
- package/tests/py/unit/test_tools_build_interface.py +30 -17
- package/tests/py/unit/test_tools_build_slab.py +21 -0
- package/tests/py/unit/test_tools_build_supercell.py +15 -0
- package/src/py/mat3ra/made/tools/build/interface.py +0 -268
- package/tests/py/unit/test_tools_build.py +0 -20
package/dist/js/cell/cell.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import CodeMath from "@mat3ra/code/dist/js/math";
|
|
1
2
|
import { Coordinate } from "../basis/types";
|
|
2
3
|
import { Vector, VectorsAsArray } from "../lattice/types";
|
|
3
|
-
type Point = Coordinate |
|
|
4
|
+
type Point = Coordinate | CodeMath.Matrix | CodeMath.MathType;
|
|
4
5
|
export declare class Cell {
|
|
5
6
|
tolerance: number;
|
|
6
7
|
vector1: Vector;
|
|
@@ -21,7 +22,7 @@ export declare class Cell {
|
|
|
21
22
|
/**
|
|
22
23
|
* Convert a point (in crystal coordinates) to cartesian.
|
|
23
24
|
*/
|
|
24
|
-
convertPointToCartesian(point: Point):
|
|
25
|
+
convertPointToCartesian(point: Point): CodeMath.MathType;
|
|
25
26
|
/**
|
|
26
27
|
* Convert a point (in cartesian coordinates) to crystal (fractional).
|
|
27
28
|
*/
|
|
@@ -83,7 +83,7 @@ export declare class Lattice extends LatticeBravais implements LatticeSchema {
|
|
|
83
83
|
beta: number;
|
|
84
84
|
gamma: number;
|
|
85
85
|
units: {
|
|
86
|
-
length?: "
|
|
86
|
+
length?: "angstrom" | "bohr" | undefined;
|
|
87
87
|
angle?: "degree" | "radian" | undefined;
|
|
88
88
|
};
|
|
89
89
|
type: "CUB" | "BCC" | "FCC" | "TET" | "MCL" | "ORC" | "ORCC" | "ORCF" | "ORCI" | "HEX" | "BCT" | "TRI" | "MCLC" | "RHL";
|
package/dist/js/material.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HasConsistencyChecksHasMetadataNamedDefaultableInMemoryEntity } from "@mat3ra/code/dist/js/entity";
|
|
2
|
-
import { AnyObject } from "@mat3ra/
|
|
2
|
+
import { AnyObject } from "@mat3ra/esse/dist/js/esse/types";
|
|
3
3
|
import { ConsistencyCheck, DerivedPropertiesSchema, FileSourceSchema, MaterialSchema } from "@mat3ra/esse/dist/js/types";
|
|
4
4
|
import { ConstrainedBasis } from "./basis/constrained_basis";
|
|
5
5
|
import { Constraint } from "./constraints/constraints";
|
|
@@ -483,9 +483,7 @@ export declare const Material: {
|
|
|
483
483
|
setProps(json?: AnyObject | undefined): any;
|
|
484
484
|
toJSON(exclude?: string[] | undefined): AnyObject;
|
|
485
485
|
toJSONSafe(exclude?: string[] | undefined): AnyObject;
|
|
486
|
-
toJSONQuick(exclude?: string[] | undefined): AnyObject;
|
|
487
|
-
* Returns a copy of the material with conventional cell constructed instead of primitive.
|
|
488
|
-
*/
|
|
486
|
+
toJSONQuick(exclude?: string[] | undefined): AnyObject;
|
|
489
487
|
clone(extraContext?: object | undefined): any;
|
|
490
488
|
validate(): void;
|
|
491
489
|
clean(config: AnyObject): AnyObject;
|
|
@@ -511,10 +509,7 @@ export declare const Material: {
|
|
|
511
509
|
toJSONQuick(exclude?: string[] | undefined): AnyObject;
|
|
512
510
|
clone(extraContext?: object | undefined): any;
|
|
513
511
|
validate(): void;
|
|
514
|
-
clean(config: AnyObject): AnyObject;
|
|
515
|
-
* @summary a series of checks for the material's basis and returns an array of results in ConsistencyChecks format.
|
|
516
|
-
* @returns Array of checks results
|
|
517
|
-
*/
|
|
512
|
+
clean(config: AnyObject): AnyObject;
|
|
518
513
|
isValid(): boolean;
|
|
519
514
|
id: string;
|
|
520
515
|
readonly cls: string;
|
package/dist/js/math.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ declare const _default: {
|
|
|
35
35
|
uninitialized: any;
|
|
36
36
|
version: string;
|
|
37
37
|
expression: import("mathjs").MathNode;
|
|
38
|
-
json: mathjs.MathJsJson;
|
|
38
|
+
json: import("mathjs").MathJsJson;
|
|
39
39
|
config: (options: any) => void;
|
|
40
40
|
lsolve(L: import("mathjs").Matrix | import("mathjs").MathArray, b: import("mathjs").Matrix | import("mathjs").MathArray): import("mathjs").Matrix | import("mathjs").MathArray;
|
|
41
41
|
lup(A?: import("mathjs").Matrix | import("mathjs").MathArray | undefined): import("mathjs").MathArray;
|
|
@@ -184,7 +184,7 @@ declare const _default: {
|
|
|
184
184
|
re(x: number | import("mathjs").Matrix | import("mathjs").BigNumber | import("mathjs").Complex | import("mathjs").MathArray): number | import("mathjs").Matrix | import("mathjs").BigNumber | import("mathjs").MathArray;
|
|
185
185
|
bignumber(x?: string | number | boolean | import("mathjs").Matrix | import("mathjs").MathArray | undefined): import("mathjs").BigNumber;
|
|
186
186
|
boolean(x: string | number | boolean | import("mathjs").Matrix | import("mathjs").MathArray): boolean | import("mathjs").Matrix | import("mathjs").MathArray;
|
|
187
|
-
chain(value?: any):
|
|
187
|
+
chain(value?: any): import("mathjs").MathJsChain;
|
|
188
188
|
complex(arg?: string | import("mathjs").Complex | import("mathjs").MathArray | import("mathjs").PolarCoordinates | undefined): import("mathjs").Complex;
|
|
189
189
|
complex(re: number, im: number): import("mathjs").Complex;
|
|
190
190
|
fraction(numerator: string | number | import("mathjs").Matrix | import("mathjs").MathArray, denominator?: string | number | import("mathjs").Matrix | import("mathjs").MathArray | undefined): import("mathjs").Matrix | import("mathjs").Fraction | import("mathjs").MathArray;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
3
|
+
const math_1 = require("@mat3ra/code/dist/js/math");
|
|
7
4
|
const constants_1 = require("../constants");
|
|
8
5
|
const lattice_1 = require("../lattice/lattice");
|
|
9
6
|
/**
|
|
@@ -40,8 +37,8 @@ function getBasisConfigTranslatedToCenter(material) {
|
|
|
40
37
|
material.toCartesian();
|
|
41
38
|
const updatedBasis = material.Basis;
|
|
42
39
|
const centerOfCoordinates = updatedBasis.centerOfCoordinatesPoint;
|
|
43
|
-
const centerOfLattice =
|
|
44
|
-
const translationVector =
|
|
40
|
+
const centerOfLattice = math_1.math.multiply(0.5, material.Lattice.vectorArrays.reduce((a, b) => math_1.math.add(a, b)));
|
|
41
|
+
const translationVector = math_1.math.subtract(centerOfLattice, centerOfCoordinates);
|
|
45
42
|
updatedBasis.translateByVector(translationVector);
|
|
46
43
|
material.setBasis(updatedBasis.toJSON());
|
|
47
44
|
if (originalUnits !== constants_1.ATOMIC_COORD_UNITS.cartesian)
|
package/dist/js/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mat3ra/made",
|
|
3
|
-
"version": "2024.
|
|
3
|
+
"version": "2024.6.4-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",
|
|
@@ -33,11 +33,10 @@
|
|
|
33
33
|
"@babel/register": "^7.22.15",
|
|
34
34
|
"@babel/runtime-corejs3": "^7.16.8",
|
|
35
35
|
"@exabyte-io/eslint-config": "^2023.8.29-1",
|
|
36
|
-
"@mat3ra/code": "2024.
|
|
37
|
-
"@mat3ra/esse": "2024.
|
|
38
|
-
"@mat3ra/tsconfig": "
|
|
36
|
+
"@mat3ra/code": "2024.6.4-0",
|
|
37
|
+
"@mat3ra/esse": "2024.6.4-1",
|
|
38
|
+
"@mat3ra/tsconfig": "2024.6.3-0",
|
|
39
39
|
"@types/crypto-js": "^4.2.2",
|
|
40
|
-
"@types/mathjs": "^3.21.1",
|
|
41
40
|
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
|
42
41
|
"@typescript-eslint/parser": "^5.9.1",
|
|
43
42
|
"chai": "^4.3.4",
|
|
@@ -72,7 +71,6 @@
|
|
|
72
71
|
"array-almost-equal": "^1.0.0",
|
|
73
72
|
"crypto-js": "4.2.0",
|
|
74
73
|
"lodash": "^4.17.*",
|
|
75
|
-
"mathjs": "12.4.1",
|
|
76
74
|
"ts-node": "^10.9.1",
|
|
77
75
|
"typescript": "^4.5.5",
|
|
78
76
|
"underscore": "^1.8.3",
|
package/src/js/cell/cell.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { math } from "@mat3ra/code/dist/js/math";
|
|
1
|
+
import CodeMath, { math } from "@mat3ra/code/dist/js/math";
|
|
2
2
|
|
|
3
3
|
import { Coordinate } from "../basis/types";
|
|
4
4
|
import constants from "../constants";
|
|
@@ -10,7 +10,7 @@ const INV = math.inv;
|
|
|
10
10
|
// @ts-ignore
|
|
11
11
|
const MATRIX_MULT = (...args) => MULT(...args.map((x) => MATRIX(x))).toArray();
|
|
12
12
|
|
|
13
|
-
type Point = Coordinate |
|
|
13
|
+
type Point = Coordinate | CodeMath.Matrix | CodeMath.MathType;
|
|
14
14
|
|
|
15
15
|
/*
|
|
16
16
|
* Cell represents a unit cell in geometrical form: 3x3 matrix where rows are cell vectors.
|
|
@@ -64,7 +64,7 @@ export class Cell {
|
|
|
64
64
|
/**
|
|
65
65
|
* Convert a point (in crystal coordinates) to cartesian.
|
|
66
66
|
*/
|
|
67
|
-
convertPointToCartesian(point: Point) {
|
|
67
|
+
convertPointToCartesian(point: Point): CodeMath.MathType {
|
|
68
68
|
return MULT(point, this.vectorsAsArray);
|
|
69
69
|
}
|
|
70
70
|
|
package/src/js/material.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HasConsistencyChecksHasMetadataNamedDefaultableInMemoryEntity } from "@mat3ra/code/dist/js/entity";
|
|
2
|
-
import { AnyObject } from "@mat3ra/
|
|
2
|
+
import { AnyObject } from "@mat3ra/esse/dist/js/esse/types";
|
|
3
3
|
import {
|
|
4
4
|
ConsistencyCheck,
|
|
5
5
|
DerivedPropertiesSchema,
|
package/src/js/tools/material.js
CHANGED
package/src/js/types.ts
CHANGED
|
@@ -55,3 +55,17 @@ def get_surface_area(atoms: Atoms):
|
|
|
55
55
|
matrix = atoms.cell
|
|
56
56
|
cross_product = np.cross(matrix[0], matrix[1])
|
|
57
57
|
return np.linalg.norm(cross_product)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@decorator_convert_material_args_kwargs_to_atoms
|
|
61
|
+
def get_chemical_formula(atoms: Atoms):
|
|
62
|
+
"""
|
|
63
|
+
Calculate the formula of the atoms structure.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
atoms (ase.Atoms): The Atoms object to calculate the formula of.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
str: The formula of the atoms.
|
|
70
|
+
"""
|
|
71
|
+
return atoms.get_chemical_formula()
|
|
@@ -1,80 +1,101 @@
|
|
|
1
|
+
from typing import List, Optional, Any
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
1
5
|
from ...material import Material
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
from ..convert import decorator_convert_material_args_kwargs_to_structure
|
|
6
|
-
from ..modify import translate_to_bottom, wrap_to_unit_cell
|
|
7
|
-
from typing import Optional
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@decorator_convert_material_args_kwargs_to_structure
|
|
11
|
-
def create_interfaces(
|
|
12
|
-
substrate: Optional[Material] = None,
|
|
13
|
-
layer: Optional[Material] = None,
|
|
14
|
-
settings: Settings = Settings(),
|
|
15
|
-
sort_by_strain_and_size: bool = True,
|
|
16
|
-
remove_duplicates: bool = True,
|
|
17
|
-
is_logging_enabled: bool = True,
|
|
18
|
-
interface_builder: Optional[CoherentInterfaceBuilder] = None,
|
|
19
|
-
termination: Optional[TerminationType] = None,
|
|
20
|
-
) -> InterfaceDataHolder:
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseBuilder(BaseModel):
|
|
21
9
|
"""
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
10
|
+
Base class for material builders.
|
|
11
|
+
This class provides an interface for generating materials and getter functions.
|
|
12
|
+
The builder is meant as a description of the process, while its functions require a
|
|
13
|
+
"Configuration" class instance to perform the generation.
|
|
14
|
+
|
|
15
|
+
The class is designed to be subclassed and the subclass should implement the following methods:
|
|
16
|
+
|
|
17
|
+
- `_generate`: Generate the material items, possibly using third-party tools/implementation for items.
|
|
18
|
+
- `_sort`: Sort the items.
|
|
19
|
+
- `_select`: Select a subset of the items.
|
|
20
|
+
- `_post_process`: Post-process the items to convert them to materials (Material class).
|
|
21
|
+
- `_finalize`: Finalize the materials.
|
|
22
|
+
|
|
23
|
+
The subclass should also define the following attributes:
|
|
24
|
+
|
|
25
|
+
- `_BuildParametersType`: The data structure model for the build parameters.
|
|
26
|
+
- `_DefaultBuildParameters`: The default build parameters.
|
|
27
|
+
- `_ConfigurationType`: The data structure model for the Configuration used during the build.
|
|
28
|
+
- `_GeneratedItemType`: The type of the generated item.
|
|
29
|
+
- `_SelectorParametersType`: The data structure model for the selector parameters.
|
|
30
|
+
- `_PostProcessParametersType`: The data structure model for the post-process parameters.
|
|
33
31
|
"""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
) ->
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
32
|
+
|
|
33
|
+
build_parameters: Any = None
|
|
34
|
+
_BuildParametersType: Any = None
|
|
35
|
+
_DefaultBuildParameters: Any = None
|
|
36
|
+
|
|
37
|
+
_ConfigurationType: Any = Any
|
|
38
|
+
_GeneratedItemType: Any = Any
|
|
39
|
+
_SelectorParametersType: Any = None
|
|
40
|
+
_PostProcessParametersType: Any = None
|
|
41
|
+
|
|
42
|
+
def __init__(self, build_parameters: _BuildParametersType = None):
|
|
43
|
+
super().__init__(build_parameters=build_parameters)
|
|
44
|
+
self.build_parameters = build_parameters or self._DefaultBuildParameters
|
|
45
|
+
self.__generated_items: List[List[BaseBuilder._GeneratedItemType]] = []
|
|
46
|
+
self.__configurations: List[BaseBuilder._ConfigurationType] = []
|
|
47
|
+
|
|
48
|
+
def _generate(self, configuration: _ConfigurationType) -> List[_GeneratedItemType]:
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
def _generate_or_get_from_cache(self, configuration: _ConfigurationType) -> List[_GeneratedItemType]:
|
|
52
|
+
if configuration not in self.__configurations:
|
|
53
|
+
self.__configurations.append(configuration)
|
|
54
|
+
self.__generated_items.append(self._generate(configuration))
|
|
55
|
+
return self.__generated_items[self.__configurations.index(configuration)]
|
|
56
|
+
|
|
57
|
+
def _sort(self, items: List[_GeneratedItemType]) -> List[_GeneratedItemType]:
|
|
58
|
+
return items
|
|
59
|
+
|
|
60
|
+
def _select(
|
|
61
|
+
self, items: List[_GeneratedItemType], selector_parameters: Optional[_SelectorParametersType]
|
|
62
|
+
) -> List[_GeneratedItemType]:
|
|
63
|
+
return items
|
|
64
|
+
|
|
65
|
+
def _post_process(
|
|
66
|
+
self, items: List[_GeneratedItemType], post_process_parameters: Optional[_PostProcessParametersType]
|
|
67
|
+
) -> List[Material]:
|
|
68
|
+
return [Material(self._convert_generated_item(item)) for item in items]
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def _convert_generated_item(item: _GeneratedItemType):
|
|
72
|
+
material_config = item
|
|
73
|
+
return material_config
|
|
74
|
+
|
|
75
|
+
def _finalize(self, materials: List[Material], configuration: _ConfigurationType) -> List[Material]:
|
|
76
|
+
return [self._update_material_name(material, configuration) for material in materials]
|
|
77
|
+
|
|
78
|
+
def get_materials(
|
|
79
|
+
self,
|
|
80
|
+
configuration: _ConfigurationType,
|
|
81
|
+
selector_parameters: Optional[_SelectorParametersType] = None,
|
|
82
|
+
post_process_parameters: Optional[_PostProcessParametersType] = None,
|
|
83
|
+
) -> List[Material]:
|
|
84
|
+
generated_items = self._generate_or_get_from_cache(configuration)
|
|
85
|
+
sorted_items = self._sort(generated_items)
|
|
86
|
+
selected_items = self._select(sorted_items, selector_parameters)
|
|
87
|
+
materials = self._post_process(selected_items, post_process_parameters)
|
|
88
|
+
finalized_materials = self._finalize(materials, configuration)
|
|
89
|
+
return finalized_materials
|
|
90
|
+
|
|
91
|
+
def get_material(
|
|
92
|
+
self,
|
|
93
|
+
configuration: _ConfigurationType,
|
|
94
|
+
selector_parameters: Optional[_SelectorParametersType] = None,
|
|
95
|
+
post_process_parameters: Optional[_PostProcessParametersType] = None,
|
|
96
|
+
) -> Material:
|
|
97
|
+
return self.get_materials(configuration, selector_parameters, post_process_parameters)[0]
|
|
98
|
+
|
|
99
|
+
def _update_material_name(self, material, configuration):
|
|
100
|
+
# Do nothing by default
|
|
101
|
+
return material
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Union, List, Optional
|
|
2
|
+
|
|
3
|
+
from mat3ra.made.material import Material
|
|
4
|
+
from .builders import (
|
|
5
|
+
SimpleInterfaceBuilder,
|
|
6
|
+
SimpleInterfaceBuilderParameters,
|
|
7
|
+
ZSLStrainMatchingParameters,
|
|
8
|
+
ZSLStrainMatchingInterfaceBuilder,
|
|
9
|
+
ZSLStrainMatchingInterfaceBuilderParameters,
|
|
10
|
+
)
|
|
11
|
+
from .configuration import InterfaceConfiguration
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_interfaces(
|
|
15
|
+
builder: Union[SimpleInterfaceBuilder, ZSLStrainMatchingInterfaceBuilder], configuration: InterfaceConfiguration
|
|
16
|
+
) -> List[Material]:
|
|
17
|
+
return builder.get_materials(configuration)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def create_interface(
|
|
21
|
+
configuration: InterfaceConfiguration,
|
|
22
|
+
builder: Optional[Union[SimpleInterfaceBuilder, ZSLStrainMatchingInterfaceBuilder]] = None,
|
|
23
|
+
) -> Material:
|
|
24
|
+
if builder is None:
|
|
25
|
+
builder = SimpleInterfaceBuilder(build_parameters=SimpleInterfaceBuilderParameters())
|
|
26
|
+
return builder.get_material(configuration)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from typing import Any, List, Optional
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
from ase.build.tools import niggli_reduce
|
|
6
|
+
from pymatgen.analysis.interfaces.coherent_interfaces import (
|
|
7
|
+
CoherentInterfaceBuilder,
|
|
8
|
+
ZSLGenerator,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from mat3ra.made.material import Material
|
|
12
|
+
from .enums import StrainModes
|
|
13
|
+
from .configuration import InterfaceConfiguration
|
|
14
|
+
from .termination_pair import TerminationPair, safely_select_termination_pair
|
|
15
|
+
from .utils import interface_patch_with_mean_abs_strain, remove_duplicate_interfaces
|
|
16
|
+
from ..mixins import (
|
|
17
|
+
ConvertGeneratedItemsASEAtomsMixin,
|
|
18
|
+
ConvertGeneratedItemsPymatgenStructureMixin,
|
|
19
|
+
)
|
|
20
|
+
from ..slab import create_slab, Termination
|
|
21
|
+
from ..slab.configuration import SlabConfiguration
|
|
22
|
+
from ...analyze import get_chemical_formula
|
|
23
|
+
from ...convert import to_ase, from_ase, to_pymatgen, PymatgenInterface, ASEAtoms
|
|
24
|
+
from ...build import BaseBuilder
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class InterfaceBuilderParameters(BaseModel):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class InterfaceBuilder(BaseBuilder):
|
|
32
|
+
_BuildParametersType = InterfaceBuilderParameters
|
|
33
|
+
_ConfigurationType: type(InterfaceConfiguration) = InterfaceConfiguration # type: ignore
|
|
34
|
+
|
|
35
|
+
def _update_material_name(self, material: Material, configuration: InterfaceConfiguration) -> Material:
|
|
36
|
+
film_formula = get_chemical_formula(configuration.film_configuration.bulk)
|
|
37
|
+
substrate_formula = get_chemical_formula(configuration.substrate_configuration.bulk)
|
|
38
|
+
film_miller_indices = "".join([str(i) for i in configuration.film_configuration.miller_indices])
|
|
39
|
+
substrate_miller_indices = "".join([str(i) for i in configuration.substrate_configuration.miller_indices])
|
|
40
|
+
new_name = f"{film_formula}({film_miller_indices})-{substrate_formula}({substrate_miller_indices}), Interface"
|
|
41
|
+
material.name = new_name
|
|
42
|
+
return material
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
########################################################################################
|
|
46
|
+
# Simple Interface Builder #
|
|
47
|
+
########################################################################################
|
|
48
|
+
class SimpleInterfaceBuilderParameters(InterfaceBuilderParameters):
|
|
49
|
+
scale_film: bool = True
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class SimpleInterfaceBuilder(ConvertGeneratedItemsASEAtomsMixin, InterfaceBuilder):
|
|
53
|
+
"""
|
|
54
|
+
Creates matching interface between substrate and film by straining the film to match the substrate.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
_BuildParametersType = Optional[SimpleInterfaceBuilderParameters]
|
|
58
|
+
_DefaultBuildParameters = SimpleInterfaceBuilderParameters(scale_film=True)
|
|
59
|
+
_GeneratedItemType: type(ASEAtoms) = ASEAtoms # type: ignore
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def __preprocess_slab_configuration(configuration: SlabConfiguration, termination: Termination):
|
|
63
|
+
slab = create_slab(configuration, termination)
|
|
64
|
+
ase_slab = to_ase(slab)
|
|
65
|
+
niggli_reduce(ase_slab)
|
|
66
|
+
return ase_slab
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def __combine_two_slabs_ase(substrate_slab_ase: ASEAtoms, film_slab_ase: ASEAtoms, distance_z: float) -> ASEAtoms:
|
|
70
|
+
max_z_substrate = max(substrate_slab_ase.positions[:, 2])
|
|
71
|
+
min_z_film = min(film_slab_ase.positions[:, 2])
|
|
72
|
+
shift_z = max_z_substrate - min_z_film + distance_z
|
|
73
|
+
|
|
74
|
+
film_slab_ase.translate([0, 0, shift_z])
|
|
75
|
+
|
|
76
|
+
return substrate_slab_ase + film_slab_ase
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def __add_vacuum_along_c_ase(interface_ase: ASEAtoms, vacuum: float) -> ASEAtoms:
|
|
80
|
+
cell_c_with_vacuum = max(interface_ase.positions[:, 2]) + vacuum
|
|
81
|
+
interface_ase.cell[2, 2] = cell_c_with_vacuum
|
|
82
|
+
return interface_ase
|
|
83
|
+
|
|
84
|
+
def _generate(self, configuration: InterfaceBuilder._ConfigurationType) -> List[_GeneratedItemType]: # type: ignore
|
|
85
|
+
film_slab_ase = self.__preprocess_slab_configuration(
|
|
86
|
+
configuration.film_configuration, configuration.film_termination
|
|
87
|
+
)
|
|
88
|
+
substrate_slab_ase = self.__preprocess_slab_configuration(
|
|
89
|
+
configuration.substrate_configuration, configuration.substrate_termination
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if self.build_parameters.scale_film:
|
|
93
|
+
film_slab_ase.set_cell(substrate_slab_ase.cell, scale_atoms=True)
|
|
94
|
+
film_slab_ase.wrap()
|
|
95
|
+
|
|
96
|
+
interface_ase = self.__combine_two_slabs_ase(substrate_slab_ase, film_slab_ase, configuration.distance_z)
|
|
97
|
+
interface_ase_with_vacuum = self.__add_vacuum_along_c_ase(interface_ase, configuration.vacuum)
|
|
98
|
+
|
|
99
|
+
return [interface_ase_with_vacuum]
|
|
100
|
+
|
|
101
|
+
def _post_process(self, items: List[_GeneratedItemType], post_process_parameters=None) -> List[Material]:
|
|
102
|
+
return [Material(from_ase(slab)) for slab in items]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
########################################################################################
|
|
106
|
+
# Strain Matching Interface Builders #
|
|
107
|
+
########################################################################################
|
|
108
|
+
class StrainMatchingInterfaceBuilderParameters(BaseModel):
|
|
109
|
+
strain_matching_parameters: Optional[Any] = None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class StrainMatchingInterfaceBuilder(InterfaceBuilder):
|
|
113
|
+
_BuildParametersType = StrainMatchingInterfaceBuilderParameters # type: ignore
|
|
114
|
+
|
|
115
|
+
def _update_material_name(self, material: Material, configuration: InterfaceConfiguration) -> Material:
|
|
116
|
+
updated_material = super()._update_material_name(material, configuration)
|
|
117
|
+
if StrainModes.mean_abs_strain in material.metadata:
|
|
118
|
+
strain = material.metadata[StrainModes.mean_abs_strain]
|
|
119
|
+
new_name = f"{updated_material.name}, Strain {strain*100:.3f}%"
|
|
120
|
+
updated_material.name = new_name
|
|
121
|
+
return material
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ZSLStrainMatchingParameters(BaseModel):
|
|
125
|
+
max_area: float = 50.0
|
|
126
|
+
max_area_ratio_tol: float = 0.09
|
|
127
|
+
max_length_tol: float = 0.03
|
|
128
|
+
max_angle_tol: float = 0.01
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ZSLStrainMatchingInterfaceBuilderParameters(StrainMatchingInterfaceBuilderParameters):
|
|
132
|
+
strain_matching_parameters: ZSLStrainMatchingParameters
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class ZSLStrainMatchingInterfaceBuilder(ConvertGeneratedItemsPymatgenStructureMixin, StrainMatchingInterfaceBuilder):
|
|
136
|
+
"""
|
|
137
|
+
Creates matching interface between substrate and film using the ZSL algorithm.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
_BuildParametersType: type( # type: ignore
|
|
141
|
+
ZSLStrainMatchingInterfaceBuilderParameters
|
|
142
|
+
) = ZSLStrainMatchingInterfaceBuilderParameters # type: ignore
|
|
143
|
+
_GeneratedItemType: PymatgenInterface = PymatgenInterface # type: ignore
|
|
144
|
+
|
|
145
|
+
def _generate(self, configuration: InterfaceConfiguration) -> List[PymatgenInterface]:
|
|
146
|
+
generator = ZSLGenerator(**self.build_parameters.strain_matching_parameters.dict())
|
|
147
|
+
builder = CoherentInterfaceBuilder(
|
|
148
|
+
substrate_structure=to_pymatgen(configuration.substrate_configuration.bulk),
|
|
149
|
+
film_structure=to_pymatgen(configuration.film_configuration.bulk),
|
|
150
|
+
substrate_miller=configuration.substrate_configuration.miller_indices,
|
|
151
|
+
film_miller=configuration.film_configuration.miller_indices,
|
|
152
|
+
zslgen=generator,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
generated_termination_pairs = [
|
|
156
|
+
TerminationPair.from_pymatgen(pymatgen_termination) for pymatgen_termination in builder.terminations
|
|
157
|
+
]
|
|
158
|
+
termination_pair = safely_select_termination_pair(configuration.termination_pair, generated_termination_pairs)
|
|
159
|
+
interfaces = builder.get_interfaces(
|
|
160
|
+
termination=termination_pair.to_pymatgen(),
|
|
161
|
+
gap=configuration.distance_z,
|
|
162
|
+
film_thickness=configuration.film_configuration.thickness,
|
|
163
|
+
substrate_thickness=configuration.substrate_configuration.thickness,
|
|
164
|
+
in_layers=True,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return list([interface_patch_with_mean_abs_strain(interface) for interface in interfaces])
|
|
168
|
+
|
|
169
|
+
def _sort(self, items: List[_GeneratedItemType]):
|
|
170
|
+
sorted_by_num_sites = sorted(items, key=lambda x: x.num_sites)
|
|
171
|
+
sorted_by_num_sites_and_strain = sorted(
|
|
172
|
+
sorted_by_num_sites, key=lambda x: np.mean(x.interface_properties[StrainModes.mean_abs_strain])
|
|
173
|
+
)
|
|
174
|
+
unique_sorted_interfaces = remove_duplicate_interfaces(
|
|
175
|
+
sorted_by_num_sites_and_strain, strain_mode=StrainModes.mean_abs_strain
|
|
176
|
+
)
|
|
177
|
+
return unique_sorted_interfaces
|
|
178
|
+
|
|
179
|
+
def _post_process(self, items: List[_GeneratedItemType], post_process_parameters=None) -> List[Material]:
|
|
180
|
+
materials = super()._post_process(items, post_process_parameters)
|
|
181
|
+
strains = [interface.interface_properties[StrainModes.mean_abs_strain] for interface in items]
|
|
182
|
+
|
|
183
|
+
for material, strain in zip(materials, strains):
|
|
184
|
+
material.metadata["mean_abs_strain"] = strain
|
|
185
|
+
return materials
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
from .termination_pair import TerminationPair
|
|
4
|
+
from ..slab import Termination
|
|
5
|
+
from ..slab.configuration import SlabConfiguration
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class InterfaceConfiguration(BaseModel):
|
|
9
|
+
film_configuration: SlabConfiguration
|
|
10
|
+
substrate_configuration: SlabConfiguration
|
|
11
|
+
film_termination: Termination
|
|
12
|
+
substrate_termination: Termination
|
|
13
|
+
distance_z: float = 3.0
|
|
14
|
+
vacuum: float = 5.0
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def termination_pair(self):
|
|
18
|
+
return TerminationPair(self.film_termination, self.substrate_termination)
|