@mat3ra/made 2026.6.29-0 → 2026.7.8-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.
Files changed (43) hide show
  1. package/dist/js/basis/basis.js +8 -8
  2. package/dist/js/basis/coordinates.js +2 -2
  3. package/dist/js/cell/cell.js +11 -10
  4. package/dist/js/cell/conventional_cell.js +2 -2
  5. package/dist/js/cell/primitive_cell.js +19 -21
  6. package/dist/js/lattice/lattice.js +42 -30
  7. package/dist/js/lattice/lattice_types.js +1 -1
  8. package/dist/js/lattice/reciprocal/lattice_reciprocal.js +16 -16
  9. package/dist/js/lattice/reciprocal/symmetry_points.js +1 -2
  10. package/dist/js/material.d.ts +15 -15
  11. package/dist/js/parsers/poscar.js +2 -4
  12. package/dist/js/parsers/xyz.js +2 -2
  13. package/dist/js/parsers/xyz_combinatorial_basis.d.ts +2 -1
  14. package/dist/js/parsers/xyz_combinatorial_basis.js +19 -9
  15. package/dist/js/tools/basis.js +3 -3
  16. package/dist/js/tools/cell.js +3 -3
  17. package/dist/js/tools/material.js +3 -3
  18. package/dist/js/tools/supercell.js +3 -3
  19. package/dist/js/tools/surface.js +14 -12
  20. package/package.json +41 -40
  21. package/pyproject.toml +6 -7
  22. package/src/js/basis/basis.ts +9 -8
  23. package/src/js/basis/coordinates.ts +3 -2
  24. package/src/js/cell/cell.ts +12 -10
  25. package/src/js/cell/primitive_cell.ts +23 -19
  26. package/src/js/lattice/lattice.ts +25 -23
  27. package/src/js/lattice/reciprocal/lattice_reciprocal.ts +19 -18
  28. package/src/js/parsers/poscar.ts +2 -2
  29. package/src/js/parsers/xyz_combinatorial_basis.ts +3 -2
  30. package/src/js/tools/basis.ts +4 -3
  31. package/src/js/tools/cell.ts +4 -3
  32. package/src/js/tools/material.ts +5 -4
  33. package/src/js/tools/supercell.ts +4 -3
  34. package/src/js/tools/surface.ts +18 -12
  35. package/src/py/mat3ra/made/cell/primitive_cell.py +156 -0
  36. package/tests/fixtures/hashes.json +3 -0
  37. package/tests/js/cell/cell.ts +1 -1
  38. package/tests/js/fixtures.ts +7 -1
  39. package/tests/js/material.test.ts +8 -1
  40. package/tests/py/conftest.py +1 -1
  41. package/tests/py/unit/test_material.py +10 -0
  42. package/tests/py/unit/test_primitive_cell.py +118 -0
  43. /package/src/py/mat3ra/made/{cell.py → cell/__init__.py} +0 -0
@@ -1,6 +1,4 @@
1
- import { HASH_TOLERANCE } from "@mat3ra/code/dist/js/constants";
2
1
  import { InMemoryEntity } from "@mat3ra/code/dist/js/entity";
3
- import { math } from "@mat3ra/code/dist/js/math";
4
2
  import {
5
3
  Coordinate3DSchema,
6
4
  LatticeSchema,
@@ -9,6 +7,7 @@ import {
9
7
  LatticeVectorsSchema,
10
8
  Matrix3X3Schema,
11
9
  } from "@mat3ra/esse/dist/js/types";
10
+ import { Utils } from "@mat3ra/utils";
12
11
  import * as lodash from "lodash";
13
12
 
14
13
  import { Cell } from "../cell/cell";
@@ -16,6 +15,7 @@ import { getPrimitiveLatticeVectorsFromConfig } from "../cell/primitive_cell";
16
15
  import { LATTICE_TYPE_CONFIGS } from "./lattice_types";
17
16
  import { UnitCell, UnitCellProps } from "./unit_cell";
18
17
 
18
+ const { HASH_TOLERANCE } = Utils.constants;
19
19
  /**
20
20
  * Scaling factor used to calculate the new lattice size for non-periodic systems.
21
21
  * The scaling factor ensures that a non-periodic structure will have have a lattice greater than the structures size.
@@ -86,19 +86,21 @@ export class Lattice extends InMemoryEntity implements LatticeSchema {
86
86
  const { b } = this;
87
87
  const { c } = this;
88
88
 
89
- const alphaRad = math.unit(this.alpha, "deg").toNumber("rad");
90
- const betaRad = math.unit(this.beta, "deg").toNumber("rad");
91
- const gammaRad = math.unit(this.gamma, "deg").toNumber("rad");
89
+ const alphaRad = Utils.math.unit(this.alpha, "deg").toNumber("rad");
90
+ const betaRad = Utils.math.unit(this.beta, "deg").toNumber("rad");
91
+ const gammaRad = Utils.math.unit(this.gamma, "deg").toNumber("rad");
92
92
 
93
- const cosAlpha = math.cos(alphaRad);
94
- const cosBeta = math.cos(betaRad);
95
- const cosGamma = math.cos(gammaRad);
96
- const sinAlpha = math.sin(alphaRad);
97
- const sinBeta = math.sin(betaRad);
93
+ const cosAlpha = Utils.math.cos(alphaRad);
94
+ const cosBeta = Utils.math.cos(betaRad);
95
+ const cosGamma = Utils.math.cos(gammaRad);
96
+ const sinAlpha = Utils.math.sin(alphaRad);
97
+ const sinBeta = Utils.math.sin(betaRad);
98
98
 
99
- const gammaStar = math.acos((cosAlpha * cosBeta - cosGamma) / (sinAlpha * sinBeta));
100
- const cosGammaStar = math.cos(gammaStar);
101
- const sinGammaStar = math.sin(gammaStar);
99
+ const gammaStar = Utils.math.acos((cosAlpha * cosBeta - cosGamma) / (sinAlpha * sinBeta));
100
+ // @ts-ignore - mathjs v12 acos return type includes Complex, but inputs are always real
101
+ const cosGammaStar = Utils.math.cos(gammaStar);
102
+ // @ts-ignore
103
+ const sinGammaStar = Utils.math.sin(gammaStar);
102
104
 
103
105
  const vectorA: Coordinate3DSchema = [a * sinBeta, 0.0, a * cosBeta];
104
106
  const vectorB: Coordinate3DSchema = [
@@ -121,12 +123,12 @@ export class Lattice extends InMemoryEntity implements LatticeSchema {
121
123
  type: LatticeTypeEnum = "TRI",
122
124
  ): Lattice {
123
125
  const [aVec, bVec, cVec] = vectors;
124
- const a = math.vlen(aVec);
125
- const b = math.vlen(bVec);
126
- const c = math.vlen(cVec);
127
- const alpha = math.angle(bVec, cVec, "deg");
128
- const beta = math.angle(aVec, cVec, "deg");
129
- const gamma = math.angle(aVec, bVec, "deg");
126
+ const a = Utils.math.vlen(aVec);
127
+ const b = Utils.math.vlen(bVec);
128
+ const c = Utils.math.vlen(cVec);
129
+ const alpha = Utils.math.angle(bVec, cVec, "deg");
130
+ const beta = Utils.math.angle(aVec, cVec, "deg");
131
+ const gamma = Utils.math.angle(aVec, bVec, "deg");
130
132
 
131
133
  return new Lattice({
132
134
  a,
@@ -183,7 +185,7 @@ export class Lattice extends InMemoryEntity implements LatticeSchema {
183
185
  */
184
186
  get typeExtended(): LatticeTypeExtendedEnum {
185
187
  const { a, b, c, alpha, beta, gamma, type } = this;
186
- const cosAlpha = math.cos((alpha / 180) * math.PI);
188
+ const cosAlpha = Utils.math.cos((alpha / 180) * Utils.math.PI);
187
189
 
188
190
  switch (type) {
189
191
  case "BCT":
@@ -220,7 +222,7 @@ export class Lattice extends InMemoryEntity implements LatticeSchema {
220
222
  * Calculate the volume of the lattice cell.
221
223
  */
222
224
  get volume(): number {
223
- return math.abs(math.det(this.vectorArrays));
225
+ return Utils.math.abs(Utils.math.det(this.vectorArrays));
224
226
  }
225
227
 
226
228
  /*
@@ -228,7 +230,7 @@ export class Lattice extends InMemoryEntity implements LatticeSchema {
228
230
  * @param latticeConfig {Object} LatticeBravais config (see constructor)
229
231
  */
230
232
  static getDefaultPrimitiveLatticeConfigByType(latticeConfig: LatticeSchema) {
231
- const f_ = math.roundArrayOrNumber;
233
+ const f_ = Utils.math.roundArrayOrNumber;
232
234
  // construct new primitive cell using lattice parameters and skip rounding the vectors
233
235
  const vectors = getPrimitiveLatticeVectorsFromConfig(latticeConfig);
234
236
  // create new lattice from primitive cell
@@ -268,7 +270,7 @@ export class Lattice extends InMemoryEntity implements LatticeSchema {
268
270
  scaledLattice.beta,
269
271
  scaledLattice.gamma,
270
272
  ]
271
- .map((x) => math.round(x, HASH_TOLERANCE))
273
+ .map((x) => Utils.math.roundCustom(x, HASH_TOLERANCE))
272
274
  .join(";")};`;
273
275
  }
274
276
 
@@ -1,12 +1,13 @@
1
1
  import { ATOMIC_COORD_UNITS, units as UNITS } from "@mat3ra/code/dist/js/constants";
2
- import { math } from "@mat3ra/code/dist/js/math";
3
2
  import { Vector3DSchema } from "@mat3ra/esse/dist/js/types";
3
+ import { Utils } from "@mat3ra/utils";
4
4
  import lodash from "lodash";
5
5
 
6
6
  import { Lattice } from "../lattice";
7
7
  import { paths } from "./paths";
8
8
  import { symmetryPoints } from "./symmetry_points";
9
9
 
10
+
10
11
  export type KPointCoordinates = number[];
11
12
  export type KPointPath = Array<{
12
13
  point: string;
@@ -39,16 +40,16 @@ export class ReciprocalLattice extends Lattice {
39
40
  */
40
41
  get reciprocalVectors(): Vector3DSchema[] {
41
42
  const vectors_: Vector3DSchema[] = this.vectors.vectorArrays as Vector3DSchema[];
42
- const a: number = math.vlen(vectors_[0]);
43
+ const a: number = Utils.math.vlen(vectors_[0]);
43
44
  const divider: number =
44
- (math.multiply(
45
+ (Utils.math.multiply(
45
46
  vectors_[0],
46
- math.cross(vectors_[1], vectors_[2]),
47
+ Utils.math.cross(vectors_[1], vectors_[2]),
47
48
  ) as unknown as number) / a;
48
49
  return [
49
- math.multiply(math.cross(vectors_[1], vectors_[2]), 1 / divider) as Vector3DSchema,
50
- math.multiply(math.cross(vectors_[2], vectors_[0]), 1 / divider) as Vector3DSchema,
51
- math.multiply(math.cross(vectors_[0], vectors_[1]), 1 / divider) as Vector3DSchema,
50
+ Utils.math.multiply(Utils.math.cross(vectors_[1], vectors_[2]), 1 / divider) as Vector3DSchema,
51
+ Utils.math.multiply(Utils.math.cross(vectors_[2], vectors_[0]), 1 / divider) as Vector3DSchema,
52
+ Utils.math.multiply(Utils.math.cross(vectors_[0], vectors_[1]), 1 / divider) as Vector3DSchema,
52
53
  ];
53
54
  }
54
55
 
@@ -56,7 +57,7 @@ export class ReciprocalLattice extends Lattice {
56
57
  * Norms of reciprocal vectors.
57
58
  */
58
59
  get reciprocalVectorNorms(): Vector3DSchema {
59
- return this.reciprocalVectors.map((vec) => math.norm(vec) as number) as Vector3DSchema;
60
+ return this.reciprocalVectors.map((vec) => Utils.math.norm(vec) as number) as Vector3DSchema;
60
61
  }
61
62
 
62
63
  /**
@@ -64,7 +65,7 @@ export class ReciprocalLattice extends Lattice {
64
65
  */
65
66
  get reciprocalVectorRatios(): Vector3DSchema {
66
67
  const norms: number[] = this.reciprocalVectorNorms;
67
- const maxNorm: number = math.max(...norms) as number;
68
+ const maxNorm: number = Utils.math.max(...norms) as number;
68
69
  return norms.map((n: number) => n / maxNorm) as Vector3DSchema;
69
70
  }
70
71
 
@@ -74,7 +75,7 @@ export class ReciprocalLattice extends Lattice {
74
75
  * @return {KPointCoordinates}
75
76
  */
76
77
  getCartesianCoordinates(point: KPointCoordinates): KPointCoordinates {
77
- return math.multiply(point, this.reciprocalVectors) as KPointCoordinates;
78
+ return Utils.math.multiply(point, this.reciprocalVectors) as KPointCoordinates;
78
79
  }
79
80
 
80
81
  /**
@@ -105,7 +106,7 @@ export class ReciprocalLattice extends Lattice {
105
106
 
106
107
  dataPoints.forEach((point: KPointCoordinates, index: number) => {
107
108
  const symmPoint: SymmetryPoint | undefined = symmPoints.find((x) => {
108
- return math.vEqualWithTolerance(x.coordinates, point, 1e-4);
109
+ return Utils.math.vEqualWithTolerance(x.coordinates, point, 1e-4);
109
110
  });
110
111
  if (symmPoint) {
111
112
  kpointPath.push({
@@ -128,10 +129,10 @@ export class ReciprocalLattice extends Lattice {
128
129
  calculateDimension(nPoints: number, index: number): number {
129
130
  const norms: number[] = this.reciprocalVectorNorms;
130
131
  const [j, k] = [0, 1, 2].filter((i) => i !== index); // get indices of other two dimensions
131
- const N: number = math.cbrt(
132
+ const N: number = Utils.math.cbrt(
132
133
  (nPoints * norms[index] ** 2) / (norms[j] * norms[k]),
133
134
  ) as number;
134
- return math.max(1, math.ceil(N)) as number;
135
+ return Utils.math.max(1, Utils.math.ceil(N)) as number;
135
136
  }
136
137
 
137
138
  /**
@@ -148,10 +149,10 @@ export class ReciprocalLattice extends Lattice {
148
149
  const { a } = this;
149
150
  return {
150
151
  [ATOMIC_COORD_UNITS.cartesian]: {
151
- [UNITS.angstrom]: (2 * math.PI) / a,
152
+ [UNITS.angstrom]: (2 * Utils.math.PI) / a,
152
153
  },
153
154
  [UNITS.angstrom]: {
154
- [ATOMIC_COORD_UNITS.cartesian]: a / (2 * math.PI),
155
+ [ATOMIC_COORD_UNITS.cartesian]: a / (2 * Utils.math.PI),
155
156
  },
156
157
  };
157
158
  }
@@ -166,7 +167,7 @@ export class ReciprocalLattice extends Lattice {
166
167
  getDimensionsFromSpacing(spacing: number, units: string = ATOMIC_COORD_UNITS.cartesian) {
167
168
  const factor: number = this.conversionTable[units][ATOMIC_COORD_UNITS.cartesian] || 1;
168
169
  return this.reciprocalVectorNorms.map((norm: number) => {
169
- return math.max(1, math.ceil(lodash.round(norm / (spacing * factor), 4))) as number;
170
+ return Utils.math.max(1, Utils.math.ceil(lodash.round(norm / (spacing * factor), 4))) as number;
170
171
  }) as Vector3DSchema;
171
172
  }
172
173
 
@@ -184,8 +185,8 @@ export class ReciprocalLattice extends Lattice {
184
185
  const norms: number[] = this.reciprocalVectorNorms;
185
186
  return (
186
187
  factor *
187
- (math.mean(
188
- dimensions.map((dim: number, i: number) => norms[i] / math.max(1, dim)),
188
+ (Utils.math.mean(
189
+ dimensions.map((dim: number, i: number) => norms[i] / Utils.math.max(1, dim)),
189
190
  ) as number)
190
191
  );
191
192
  }
@@ -1,4 +1,3 @@
1
- import { math } from "@mat3ra/code/dist/js/math";
2
1
  import {
3
2
  BasisSchema,
4
3
  Coordinate3DSchema,
@@ -16,7 +15,8 @@ import { ATOMIC_COORD_UNITS } from "../constants";
16
15
  import { AtomicConstraintValue } from "../constraints/constraints";
17
16
  import { Lattice } from "../lattice/lattice";
18
17
 
19
- const _print = (x: number, printFormat = "%14.9f") => s.sprintf(printFormat, math.precise(x));
18
+
19
+ const _print = (x: number, printFormat = "%14.9f") => s.sprintf(printFormat, Utils.math.precise(x));
20
20
  const _latticeVectorsToString = (vectors: Vector3DSchema[]) =>
21
21
  vectors.map((v) => v.map((c) => _print(c)).join("\t")).join("\n");
22
22
  const atomicConstraintsCharFromBool = (bool: boolean): string => (bool ? "T" : "F");
@@ -1,5 +1,5 @@
1
- import { math } from "@mat3ra/code/dist/js/math";
2
1
  import { BasisSchema } from "@mat3ra/esse/dist/js/types";
2
+ import { Utils } from "@mat3ra/utils";
3
3
  import { chain, last, map } from "lodash";
4
4
  import * as s from "underscore.string";
5
5
 
@@ -8,6 +8,7 @@ import { AtomicCoordinateValue } from "../basis/coordinates";
8
8
  import { AtomicElementValue } from "../basis/elements";
9
9
  import { Cell } from "../cell/cell";
10
10
 
11
+
11
12
  /**
12
13
  * @summary Combinatorial XYZ basis class and related. Create and get all information about basis and elements in it.
13
14
  * Constructor accepts string in extended XYZ format. Extended XYZ format is as follows:
@@ -224,7 +225,7 @@ export class CombinatorialBasis {
224
225
  dimensions.push(itemsSet);
225
226
  });
226
227
  // @ts-ignore // We're multiplying objects with math, not numbers. No type casting will help.
227
- const basisSet = math.cartesianProduct.apply(null, dimensions) as ElementWithCoordinate[][];
228
+ const basisSet = Utils.math.cartesianProduct.apply(null, dimensions) as ElementWithCoordinate[][];
228
229
  return basisSet.map((basis: ElementWithCoordinate[]) =>
229
230
  basis.filter((entry) => entry.element !== VACANCY_CHARACTER),
230
231
  );
@@ -1,12 +1,13 @@
1
- import { math } from "@mat3ra/code/dist/js/math";
2
1
  import { Coordinate3DSchema } from "@mat3ra/esse/dist/js/types";
2
+ import { Utils } from "@mat3ra/utils";
3
3
  import { chunk, flatten } from "lodash";
4
4
 
5
5
  import { Basis } from "../basis/basis";
6
6
  import { AtomicCoordinateValue, Coordinate } from "../basis/coordinates";
7
7
 
8
- const ADD = math.add;
9
- const MULT = math.multiply;
8
+
9
+ const ADD = Utils.math.add;
10
+ const MULT = Utils.math.multiply;
10
11
 
11
12
  /**
12
13
  * Returns a repeated basis of a crystal.
@@ -1,8 +1,9 @@
1
- import { math } from "@mat3ra/code/dist/js/math";
2
1
  import { Coordinate3DSchema, Matrix3X3Schema } from "@mat3ra/esse/dist/js/types";
2
+ import { Utils } from "@mat3ra/utils";
3
3
 
4
4
  import { Cell } from "../cell/cell";
5
5
 
6
+
6
7
  /**
7
8
  * Returns the list of points on the original lattice contained in the supercell in fractional coordinates.
8
9
  * Source: https://pymatgen.org/_modules/pymatgen/util/coord.html
@@ -22,8 +23,8 @@ function latticePointsInSupercell(supercellMatrix: Matrix3X3Schema) {
22
23
  const d_points = diagonals.map((point) =>
23
24
  supercell.convertPointToCartesian(point as Coordinate3DSchema),
24
25
  );
25
- const mins = [0, 1, 2].map((i) => math.min(...d_points.map((p) => p[i])));
26
- const maxes = [0, 1, 2].map((i) => math.max(...d_points.map((p) => p[i])) + 1);
26
+ const mins = [0, 1, 2].map((i) => Utils.math.min(...d_points.map((p) => p[i])));
27
+ const maxes = [0, 1, 2].map((i) => Utils.math.max(...d_points.map((p) => p[i])) + 1);
27
28
  const points = [];
28
29
  for (let i = mins[0]; i <= maxes[0]; i++) {
29
30
  for (let j = mins[1]; j <= maxes[1]; j++) {
@@ -1,10 +1,11 @@
1
- import { math } from "@mat3ra/code/dist/js/math";
2
1
  import { LatticeSchema, Vector3DSchema } from "@mat3ra/esse/dist/js/types";
2
+ import { Utils } from "@mat3ra/utils";
3
3
 
4
4
  import { ATOMIC_COORD_UNITS } from "../constants";
5
5
  import { Lattice } from "../lattice/lattice";
6
6
  import { Material } from "../material";
7
7
 
8
+
8
9
  /**
9
10
  * Scales one lattice vector for the given material
10
11
  * @param material {Material} The material acted upon.
@@ -44,11 +45,11 @@ function translateAtomsToCenter(material: Material) {
44
45
  material.toCartesian();
45
46
  const updatedBasis = material.Basis;
46
47
  const centerOfCoordinates = updatedBasis.centerOfCoordinatesPoint;
47
- const centerOfLattice = math.multiply(
48
+ const centerOfLattice = Utils.math.multiply(
48
49
  0.5,
49
- material.Lattice.vectorArrays.reduce((a, b) => math.add(a, b) as Vector3DSchema),
50
+ material.Lattice.vectorArrays.reduce((a, b) => Utils.math.add(a, b) as Vector3DSchema),
50
51
  );
51
- const translationVector = math.subtract(centerOfLattice, centerOfCoordinates);
52
+ const translationVector = Utils.math.subtract(centerOfLattice, centerOfCoordinates);
52
53
  updatedBasis.translateByVector(translationVector as Vector3DSchema);
53
54
  material.setBasis(updatedBasis.toJSON());
54
55
  if (originalUnits !== ATOMIC_COORD_UNITS.cartesian) material.toCrystal();
@@ -1,5 +1,5 @@
1
- import { math } from "@mat3ra/code/dist/js/math";
2
1
  import { Coordinate3DSchema, Matrix3X3Schema } from "@mat3ra/esse/dist/js/types";
2
+ import { Utils } from "@mat3ra/utils";
3
3
 
4
4
  import { Basis } from "../basis/basis";
5
5
  import { ConstrainedBasis } from "../basis/constrained_basis";
@@ -8,7 +8,8 @@ import { Lattice } from "../lattice/lattice";
8
8
  import type { Material } from "../material";
9
9
  import cellTools from "./cell";
10
10
 
11
- const ADD = math.add;
11
+
12
+ const ADD = Utils.math.add;
12
13
 
13
14
  /**
14
15
  * @summary Generates new basis for a supercell. For each site from basis generates shifts that are within supercell.
@@ -56,7 +57,7 @@ function generateNewBasisWithinSupercell(
56
57
  * @param supercellMatrix {Number[][]}
57
58
  */
58
59
  function generateConfig(material: Material, supercellMatrix: Matrix3X3Schema) {
59
- const det = math.det(supercellMatrix);
60
+ const det = Utils.math.det(supercellMatrix);
60
61
  if (det === 0) {
61
62
  throw new Error("Scaling matrix is degenerate.");
62
63
  }
@@ -1,21 +1,21 @@
1
- import { math } from "@mat3ra/code/dist/js/math";
2
1
  import {
3
2
  Coordinate3DSchema,
4
3
  MaterialSchema,
5
4
  Matrix3X3Schema,
6
5
  Vector3DSchema,
7
6
  } from "@mat3ra/esse/dist/js/types";
7
+ import { Utils } from "@mat3ra/utils";
8
8
 
9
9
  import { Cell } from "../cell/cell";
10
10
  import { Lattice } from "../lattice/lattice";
11
11
  import { Material } from "../material";
12
12
  import SupercellTools from "./supercell";
13
13
 
14
- const MULT = math.multiply;
15
- const ADD = math.add;
16
- const DOT = math.product;
14
+ const MULT = Utils.math.multiply;
15
+ const ADD = Utils.math.add;
16
+ const DOT = Utils.math.product;
17
17
  const getMatrixInLeftHandedRepresentation = (matrix: Matrix3X3Schema) => {
18
- return math.det(matrix) < 0 ? MULT(matrix, -1) : matrix;
18
+ return Utils.math.det(matrix) < 0 ? MULT(matrix, -1) : matrix;
19
19
  };
20
20
 
21
21
  /**
@@ -27,10 +27,10 @@ const getMatrixInLeftHandedRepresentation = (matrix: Matrix3X3Schema) => {
27
27
  */
28
28
  function extGCD(a: number, b: number): [number, number] {
29
29
  if (b === 0) return [1, 0];
30
- if (math.mod(a, b) === 0) return [0, 1];
30
+ if (Utils.math.mod(a, b) === 0) return [0, 1];
31
31
 
32
- const [x, y] = extGCD(b, math.mod(a, b));
33
- return [y, x - y * math.floor(a / b)];
32
+ const [x, y] = extGCD(b, Utils.math.mod(a, b));
33
+ return [y, x - y * Utils.math.floor(a / b)];
34
34
  }
35
35
 
36
36
  /**
@@ -46,7 +46,7 @@ function getMillerScalingMatrix(
46
46
  millerIndices: Coordinate3DSchema,
47
47
  tol = 1e-8,
48
48
  ): Matrix3X3Schema {
49
- if (!millerIndices.reduce((a, b) => math.abs(a) + math.abs(b)))
49
+ if (!millerIndices.reduce((a, b) => Utils.math.abs(a) + Utils.math.abs(b)))
50
50
  throw new Error("Miller indices are zeros.");
51
51
 
52
52
  let scalingMatrix;
@@ -94,20 +94,26 @@ function getMillerScalingMatrix(
94
94
  // @ts-ignore
95
95
  const k2 = DOT(ADD(MULT(l, z1), -MULT(k, z2)), z3);
96
96
 
97
- if (math.abs(k2) > tol) {
97
+ // @ts-ignore
98
+ if (Utils.math.abs(k2) > tol) {
98
99
  // For mathjs version 3.20: round(-0.5) = -0
99
100
  // For mathjs version 5.10: round(-0.5) = -1
100
101
  // Here we specify rounding method to Bankers
101
102
  // For Python 3.11: round(-0.5) = 0
103
+ // @ts-ignore - mathjs v12 dot return type
102
104
  const value = k1 / k2;
103
- const roundedValue = math.roundCustom(value, 0, math.RoundingMethod.Bankers);
105
+ const roundedValue = Utils.math.roundCustom(
106
+ value,
107
+ 0,
108
+ Utils.math.RoundingMethod.Bankers,
109
+ );
104
110
  const i = -roundedValue;
105
111
  [p, q] = [p + i * l, q - i * k];
106
112
  }
107
113
 
108
114
  const [a, b] = extGCD(p * k + q * l, h);
109
115
  const c1 = [p * k + q * l, -p * h, -q * h];
110
- const c2 = [0, l, -k].map((c) => math.trunc(c / math.gcd(l, k))); // floor division
116
+ const c2 = [0, l, -k].map((c) => Utils.math.trunc(c / Utils.math.gcd(l, k))); // floor division
111
117
  const c3 = [b, a * p, a * q];
112
118
 
113
119
  scalingMatrix = [c1, c2, c3];
@@ -0,0 +1,156 @@
1
+ """
2
+ Routines for calculating primitive cell vectors from conventional cell Bravais parameters.
3
+ Python equivalent of src/js/cell/primitive_cell.ts.
4
+
5
+ Following Setyawan, W., & Curtarolo, S. (2010). doi:10.1016/j.commatsci.2010.05.010
6
+ """
7
+
8
+ import math
9
+ from typing import List
10
+
11
+ from mat3ra.esse.models.properties_directory.structural.lattice import LatticeSchema
12
+ from mat3ra.made.cell import Cell
13
+
14
+ Matrix3x3 = List[List[float]]
15
+
16
+
17
+ def _rhl(p: LatticeSchema) -> Matrix3x3:
18
+ a = p.a
19
+ alpha_rad = math.radians(p.alpha)
20
+
21
+ cos_alpha = math.cos(alpha_rad)
22
+ cos_half_alpha = math.cos(alpha_rad / 2.0)
23
+ sin_half_alpha = math.sin(alpha_rad / 2.0)
24
+
25
+ z_comp = a * math.sqrt(1.0 - (cos_alpha / cos_half_alpha) ** 2)
26
+
27
+ return [
28
+ [a * cos_half_alpha, -a * sin_half_alpha, 0.0],
29
+ [a * cos_half_alpha, a * sin_half_alpha, 0.0],
30
+ [(a * cos_alpha) / cos_half_alpha, 0.0, z_comp],
31
+ ]
32
+
33
+
34
+ def _tri(p: LatticeSchema) -> Matrix3x3:
35
+ """
36
+ Algorithm from pymatgen (from_params).
37
+ Mirrors the TRI case in primitive_cell.ts exactly.
38
+ """
39
+ alpha = math.radians(p.alpha)
40
+ beta = math.radians(p.beta)
41
+ gamma = math.radians(p.gamma)
42
+
43
+ cos_alpha = math.cos(alpha)
44
+ cos_beta = math.cos(beta)
45
+ cos_gamma = math.cos(gamma)
46
+ sin_alpha = math.sin(alpha)
47
+ sin_beta = math.sin(beta)
48
+
49
+ acos_arg = (cos_alpha * cos_beta - cos_gamma) / (sin_alpha * sin_beta)
50
+ # Clamp to [-1, 1] to guard against floating-point drift
51
+ acos_arg = max(-1.0, min(1.0, acos_arg))
52
+
53
+ gamma_star = math.acos(acos_arg)
54
+ cos_gamma_star = math.cos(gamma_star)
55
+ sin_gamma_star = math.sin(gamma_star)
56
+
57
+ a, b, c = p.a, p.b, p.c
58
+ return [
59
+ [a * sin_beta, 0.0, a * cos_beta],
60
+ [-b * sin_alpha * cos_gamma_star, b * sin_alpha * sin_gamma_star, b * cos_alpha],
61
+ [0.0, 0.0, c],
62
+ ]
63
+
64
+
65
+ PRIMITIVE_CELLS = {
66
+ "CUB": lambda p: [
67
+ [p.a, 0, 0],
68
+ [0, p.a, 0],
69
+ [0, 0, p.a],
70
+ ],
71
+ "FCC": lambda p: [
72
+ [0.0, p.a / 2, p.a / 2],
73
+ [p.a / 2, 0.0, p.a / 2],
74
+ [p.a / 2, p.a / 2, 0.0],
75
+ ],
76
+ "BCC": lambda p: [
77
+ [-p.a / 2, p.a / 2, p.a / 2],
78
+ [p.a / 2, -p.a / 2, p.a / 2],
79
+ [p.a / 2, p.a / 2, -p.a / 2],
80
+ ],
81
+ "TET": lambda p: [
82
+ [p.a, 0, 0],
83
+ [0, p.a, 0],
84
+ [0, 0, p.c],
85
+ ],
86
+ "BCT": lambda p: [
87
+ [-p.a / 2, p.a / 2, p.c / 2],
88
+ [p.a / 2, -p.a / 2, p.c / 2],
89
+ [p.a / 2, p.a / 2, -p.c / 2],
90
+ ],
91
+ "ORC": lambda p: [
92
+ [p.a, 0, 0],
93
+ [0, p.b, 0],
94
+ [0, 0, p.c],
95
+ ],
96
+ "ORCF": lambda p: [
97
+ [0, p.b / 2, p.c / 2],
98
+ [p.a / 2, 0, p.c / 2],
99
+ [p.a / 2, p.b / 2, 0],
100
+ ],
101
+ "ORCI": lambda p: [
102
+ [-p.a / 2, p.b / 2, p.c / 2],
103
+ [p.a / 2, -p.b / 2, p.c / 2],
104
+ [p.a / 2, p.b / 2, -p.c / 2],
105
+ ],
106
+ "ORCC": lambda p: [
107
+ [p.a / 2, p.b / 2, 0],
108
+ [-p.a / 2, p.b / 2, 0],
109
+ [0, 0, p.c],
110
+ ],
111
+ "HEX": lambda p: [
112
+ [p.a / 2, -(p.a * math.sqrt(3)) / 2, 0],
113
+ [p.a / 2, (p.a * math.sqrt(3)) / 2, 0],
114
+ [0, 0, p.c],
115
+ ],
116
+ "RHL": _rhl,
117
+ "MCL": lambda p: [
118
+ [p.a, 0, 0],
119
+ [0, p.b, 0],
120
+ [0, p.c * math.cos(math.radians(p.alpha)), p.c * math.sin(math.radians(p.alpha))],
121
+ ],
122
+ "MCLC": lambda p: [
123
+ [p.a / 2, p.b / 2, 0],
124
+ [-p.a / 2, p.b / 2, 0],
125
+ [0, p.c * math.cos(math.radians(p.alpha)), p.c * math.sin(math.radians(p.alpha))],
126
+ ],
127
+ "TRI": _tri,
128
+ }
129
+
130
+
131
+ def get_primitive_lattice_vectors_from_config(lattice_config: LatticeSchema) -> Matrix3x3:
132
+ """
133
+ Returns lattice vectors for a primitive cell for a given lattice config.
134
+ Python equivalent of getPrimitiveLatticeVectorsFromConfig() in primitive_cell.ts.
135
+
136
+ Args:
137
+ lattice_config: LatticeSchema instance with fields:
138
+ type, a, b, c, alpha, beta, gamma.
139
+
140
+ Returns:
141
+ 3x3 list of lists (rows = vectors a, b, c) in the same units as the
142
+ input lattice parameters (typically Angstroms).
143
+ """
144
+ lattice_type = lattice_config.type.value if lattice_config.type else "TRI"
145
+ generator = PRIMITIVE_CELLS.get(lattice_type)
146
+ if generator is None:
147
+ raise ValueError(
148
+ f"Unsupported lattice type '{lattice_type}'. " f"Must be one of: {list(PRIMITIVE_CELLS.keys())}"
149
+ )
150
+ return generator(lattice_config)
151
+
152
+
153
+ def get_primitive_cell_from_config(lattice_config: LatticeSchema) -> Cell:
154
+ """Returns a full Cell object instantiated with the primitive lattice vectors."""
155
+ vectors = get_primitive_lattice_vectors_from_config(lattice_config)
156
+ return Cell.from_vectors_array(vectors)
@@ -0,0 +1,3 @@
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:823cf1e89c00beb0df0799a86573d038f725f7d7612bab4e9e40df5dab3330d8
3
+ size 132
@@ -41,7 +41,7 @@ const VECTORS_EQUAL_UP_TO_PRECISION_4: Matrix3X3Schema = [
41
41
  [0.0, 0.0, 3.00001],
42
42
  ];
43
43
 
44
- const VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME = 6.000110000600002;
44
+ const VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME = 6.000110000600001;
45
45
  const VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME_ROUNDED_TO_PRECISION_4 = 6.0001;
46
46
  const VECTORS_EQUAL_UP_TO_PRECISION_4_VOLUME_ROUNDED_TO_PRECISION_5 = 6.00011;
47
47
 
@@ -1,14 +1,15 @@
1
1
  import { AtomicConstraintsSchema, BasisSchema, MaterialSchema } from "@mat3ra/esse/dist/js/types";
2
+ import { MaterialStandata } from "@mat3ra/standata";
2
3
  import fs from "fs";
3
4
  import path from "path";
4
5
 
5
- // TODO: use @mat3ra/standata
6
6
  import AsGeBasis_JSON from "../fixtures/AsGe-basis.json";
7
7
  import atomicConstraints_JSON from "../fixtures/atomic-constraints.json";
8
8
  import C2H4_JSON from "../fixtures/C2H4.json";
9
9
  import C2H4Translated_JSON from "../fixtures/C2H4-translated.json";
10
10
  import FeLiSiBasis_JSON from "../fixtures/FeLiSi-basis.json";
11
11
  import FeO_JSON from "../fixtures/FeO.json";
12
+
12
13
  import Ge2Basis_JSON from "../fixtures/Ge2-basis.json";
13
14
  import Graphene_JSON from "../fixtures/Graphene.json";
14
15
  import H2HFinal_JSON from "../fixtures/H2+H-final.json";
@@ -27,6 +28,7 @@ import SiSlab111Gamma120_JSON from "../fixtures/si-slab-111-0.5-vacuum-gamma-120
27
28
  import SiSlab111_JSON from "../fixtures/si-slab-111-0.5-vacuum-ratio.json";
28
29
  import SiSlab111NoVacuum_JSON from "../fixtures/si-slab-111-0-vacuum.json";
29
30
  import Silicon_JSON from "../fixtures/si-standata.json";
31
+
30
32
  import SiSupercell_JSON from "../fixtures/Si-supercell.json";
31
33
  import Si2Basis_JSON from "../fixtures/Si2-basis.json";
32
34
  import Si2BasisRepeated_JSON from "../fixtures/Si2-basis-repeated.json";
@@ -41,6 +43,9 @@ const SiSlab111 = SiSlab111_JSON as unknown as MaterialSchema;
41
43
  const SiSlab111Gamma120 = SiSlab111Gamma120_JSON as unknown as MaterialSchema;
42
44
  const SiSlab111NoVacuum = SiSlab111NoVacuum_JSON as unknown as MaterialSchema;
43
45
  const FeO = FeO_JSON as unknown as MaterialSchema;
46
+ const FeOStandata = MaterialStandata.getRuntimeDataConfigs().find(
47
+ (m: any) => m.name?.includes("FeO"),
48
+ ) as MaterialSchema;
44
49
  const Na4Cl4 = Na4Cl4_JSON as unknown as MaterialSchema;
45
50
  const Na4Cl4Cartesian = Na4Cl4Cartesian_JSON as unknown as MaterialSchema;
46
51
  const C2H4 = C2H4_JSON as unknown as MaterialSchema;
@@ -79,6 +84,7 @@ export {
79
84
  Si2BasisRepeated,
80
85
  // Other
81
86
  FeO,
87
+ FeOStandata,
82
88
  Na4Cl4,
83
89
  Na4Cl4Cartesian,
84
90
  C2H4,