@mat3ra/made 2026.8.7-0 → 2026.8.13-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 (66) hide show
  1. package/.mocharc.json +2 -2
  2. package/dist/js/Material.d.ts +219 -0
  3. package/dist/js/Material.js +457 -0
  4. package/dist/js/basis/basis.d.ts +14 -9
  5. package/dist/js/basis/basis.js +15 -13
  6. package/dist/js/basis/constrained_basis.d.ts +9 -7
  7. package/dist/js/basis/constrained_basis.js +4 -4
  8. package/dist/js/generated/MaterialSchemaMixin.d.ts +5 -0
  9. package/dist/js/generated/MaterialSchemaMixin.js +69 -0
  10. package/dist/js/lattice/lattice.d.ts +4 -3
  11. package/dist/js/lattice/lattice.js +1 -2
  12. package/dist/js/made.d.ts +12 -12
  13. package/dist/js/made.js +38 -5
  14. package/dist/js/parsers/espresso.d.ts +2 -3
  15. package/dist/js/parsers/espresso.js +0 -1
  16. package/dist/js/parsers/native_format_parsers.d.ts +9 -8
  17. package/dist/js/parsers/native_format_parsers.js +6 -6
  18. package/dist/js/parsers/parsers.d.ts +6 -6
  19. package/dist/js/parsers/poscar.d.ts +5 -4
  20. package/dist/js/parsers/poscar.js +3 -2
  21. package/dist/js/parsers/xyz.d.ts +3 -5
  22. package/dist/js/parsers/xyz.js +3 -4
  23. package/dist/js/parsers/xyz_combinatorial_basis.js +3 -1
  24. package/dist/js/tools/basis.d.ts +1 -1
  25. package/dist/js/tools/index.d.ts +7 -7
  26. package/dist/js/tools/material.d.ts +1 -1
  27. package/dist/js/tools/material.js +8 -7
  28. package/dist/js/tools/supercell.d.ts +4 -5
  29. package/dist/js/tools/supercell.js +2 -4
  30. package/dist/js/tools/surface.d.ts +4 -4
  31. package/dist/js/tools/surface.js +2 -7
  32. package/package.json +6 -6
  33. package/scripts/generate-mixins.ts +28 -0
  34. package/src/js/Material.ts +618 -0
  35. package/src/js/basis/basis.ts +38 -25
  36. package/src/js/basis/constrained_basis.ts +12 -11
  37. package/src/js/basis/coordinates.ts +0 -1
  38. package/src/js/cell/cell.ts +0 -1
  39. package/src/js/generated/MaterialSchemaMixin.ts +79 -0
  40. package/src/js/lattice/lattice.ts +6 -4
  41. package/src/js/lattice/reciprocal/lattice_reciprocal.ts +19 -6
  42. package/src/js/made.ts +1 -1
  43. package/src/js/parsers/espresso.ts +2 -3
  44. package/src/js/parsers/native_format_parsers.ts +9 -8
  45. package/src/js/parsers/poscar.ts +12 -6
  46. package/src/js/parsers/xyz.ts +9 -6
  47. package/src/js/parsers/xyz_combinatorial_basis.ts +5 -2
  48. package/src/js/tools/basis.ts +0 -1
  49. package/src/js/tools/cell.ts +0 -1
  50. package/src/js/tools/material.ts +13 -11
  51. package/src/js/tools/supercell.ts +8 -7
  52. package/src/js/tools/surface.ts +7 -17
  53. package/src/py/mat3ra/made/material.py +6 -5
  54. package/tests/fixtures/si-standata.json +2 -2
  55. package/tests/js/basis/basis.ts +8 -8
  56. package/tests/js/fixtures.ts +12 -9
  57. package/tests/js/material/material.ts +161 -0
  58. package/tests/js/parsers/espresso.ts +2 -5
  59. package/tests/js/parsers/poscar.ts +2 -2
  60. package/tests/js/register-esse-schemas.cjs +10 -0
  61. package/tests/js/tools/supercell.ts +2 -2
  62. package/tests/js/tools/surface.ts +6 -10
  63. package/dist/js/material.d.ts +0 -169
  64. package/dist/js/material.js +0 -362
  65. package/src/js/material.ts +0 -458
  66. package/tests/js/material.test.ts +0 -42
@@ -1,458 +0,0 @@
1
- import { type DefaultableInMemoryEntity, InMemoryEntity } from "@mat3ra/code/dist/js/entity";
2
- import { defaultableEntityMixin } from "@mat3ra/code/dist/js/entity/mixins/DefaultableMixin";
3
- import {
4
- type HasConsistencyChecks,
5
- hasConsistencyChecksMixin,
6
- } from "@mat3ra/code/dist/js/entity/mixins/HasConsistencyChecksMixin";
7
- import {
8
- type HasMetadata,
9
- hasMetadataMixin,
10
- } from "@mat3ra/code/dist/js/entity/mixins/HasMetadataMixin";
11
- import type { AnyObject } from "@mat3ra/esse/dist/js/esse/types";
12
- import type {
13
- AtomicConstraintsSchema,
14
- ConsistencyCheck,
15
- DerivedPropertiesSchema,
16
- FileSourceSchema,
17
- InChIRepresentationSchema,
18
- LatticeSchema,
19
- MaterialSchema,
20
- } from "@mat3ra/esse/dist/js/types";
21
- import CryptoJS from "crypto-js";
22
-
23
- import type { BasisConfig } from "./basis/basis";
24
- import { type ConstrainedBasisConfig, ConstrainedBasis } from "./basis/constrained_basis";
25
- import {
26
- isConventionalCellSameAsPrimitiveForLatticeType,
27
- PRIMITIVE_TO_CONVENTIONAL_CELL_LATTICE_TYPES,
28
- PRIMITIVE_TO_CONVENTIONAL_CELL_MULTIPLIERS,
29
- } from "./cell/conventional_cell";
30
- import { Constraint } from "./constraints/constraints";
31
- import { Lattice } from "./lattice/lattice";
32
- import parsers from "./parsers/parsers";
33
- import supercellTools from "./tools/supercell";
34
-
35
- interface Material
36
- extends HasConsistencyChecks,
37
- DefaultableInMemoryEntity,
38
- Required<HasMetadata<MaterialSchema["metadata"]>> {}
39
-
40
- // TODO: remove in-line type creation
41
- type MaterialSchemaWithConsistencyChecksAsString = Omit<MaterialSchema, "consistencyChecks"> & {
42
- consistencyChecks?: ConsistencyCheck[];
43
- };
44
-
45
- type OptionallyConstrainedBasisConfig = BasisConfig &
46
- Partial<Pick<ConstrainedBasisConfig, "constraints">>;
47
-
48
- type Schema = MaterialSchemaWithConsistencyChecksAsString;
49
-
50
- export const defaultMaterialConfig: MaterialSchema = {
51
- name: "Silicon FCC",
52
- basis: {
53
- elements: [
54
- {
55
- id: 0,
56
- value: "Si",
57
- },
58
- {
59
- id: 1,
60
- value: "Si",
61
- },
62
- ],
63
- coordinates: [
64
- {
65
- id: 0,
66
- value: [0.0, 0.0, 0.0],
67
- },
68
- {
69
- id: 1,
70
- value: [0.25, 0.25, 0.25],
71
- },
72
- ],
73
- units: "crystal",
74
- },
75
- lattice: {
76
- // Primitive cell for Diamond FCC Silicon at ambient conditions
77
- type: "FCC",
78
- a: 3.867,
79
- b: 3.867,
80
- c: 3.867,
81
- alpha: 60,
82
- beta: 60,
83
- gamma: 60,
84
- units: {
85
- length: "angstrom",
86
- angle: "degree",
87
- },
88
- },
89
- metadata: {},
90
- };
91
-
92
- class Material extends InMemoryEntity implements Schema {
93
- declare static createDefault: () => Material;
94
-
95
- static get defaultConfig() {
96
- return defaultMaterialConfig;
97
- }
98
-
99
- static constructMaterialFileSource(
100
- fileName: string,
101
- fileContent: string,
102
- fileExtension: string,
103
- ): FileSourceSchema {
104
- return {
105
- extension: fileExtension,
106
- filename: fileName,
107
- text: fileContent,
108
- hash: CryptoJS.MD5(fileContent).toString(),
109
- };
110
- }
111
-
112
- get name() {
113
- return this.prop("name") ?? this.formula; // if name is not set, use formula, but keep empty string
114
- }
115
-
116
- set name(name: string) {
117
- this.setProp("name", name);
118
- }
119
-
120
- get src() {
121
- return this.prop("src");
122
- }
123
-
124
- set src(src: FileSourceSchema | undefined) {
125
- this.setProp("src", src);
126
- }
127
-
128
- updateFormula() {
129
- this.setProp("formula", this.Basis.formula);
130
- this.setProp("unitCellFormula", this.Basis.unitCellFormula);
131
- }
132
-
133
- /**
134
- * Gets Bolean value for whether or not a material is non-periodic vs periodic.
135
- * False = periodic, True = non-periodic
136
- */
137
- get isNonPeriodic(): boolean {
138
- return this.prop("isNonPeriodic", false);
139
- }
140
-
141
- /**
142
- * @summary Sets the value of isNonPeriodic based on Boolean value passed as an argument.
143
- */
144
- set isNonPeriodic(bool: boolean) {
145
- this.setProp("isNonPeriodic", bool);
146
- }
147
-
148
- /**
149
- * @summary Returns the specific derived property (as specified by name) for a material.
150
- */
151
- getDerivedPropertyByName(name: string) {
152
- return this.getDerivedProperties().find((x) => x.name === name);
153
- }
154
-
155
- /**
156
- * @summary Returns the derived properties array for a material.
157
- */
158
- getDerivedProperties(): DerivedPropertiesSchema {
159
- return this.prop("derivedProperties", []);
160
- }
161
-
162
- /**
163
- * Gets material's formula
164
- */
165
- get formula(): string {
166
- return this.prop("formula") || this.Basis.formula;
167
- }
168
-
169
- get unitCellFormula(): string {
170
- return this.prop("unitCellFormula") || this.Basis.unitCellFormula;
171
- }
172
-
173
- unsetFileProps() {
174
- this.unsetProp("src");
175
- this.unsetProp("icsdId");
176
- this.unsetProp("external");
177
- }
178
-
179
- /**
180
- * @param textOrObject Basis text or JSON object.
181
- * @param format Format (xyz, etc.)
182
- * @param unitz crystal/cartesian
183
- */
184
- setBasis(textOrObject: string | BasisConfig, format?: string, unitz?: string) {
185
- let basis: BasisConfig | undefined;
186
- if (typeof textOrObject === "string" && format === "xyz") {
187
- basis = parsers.xyz.toBasisConfig(textOrObject, unitz);
188
- } else {
189
- basis = textOrObject as BasisConfig;
190
- }
191
- this.setProp("basis", basis);
192
- this.unsetFileProps();
193
- this.updateFormula();
194
- }
195
-
196
- setBasisConstraints(constraints: Constraint[]) {
197
- const basisWithConstraints = {
198
- ...this.basis,
199
- constraints: constraints.map((c) => c.toJSON()),
200
- };
201
- this.setBasis(basisWithConstraints);
202
- }
203
-
204
- setBasisConstraintsFromArrayOfObjects(constraints: AtomicConstraintsSchema) {
205
- const constraintsInstances = constraints.map((c) => {
206
- return Constraint.fromValueAndId(c.value, c.id);
207
- });
208
- this.setBasisConstraints(constraintsInstances);
209
- }
210
-
211
- get basis() {
212
- return this.requiredProp<OptionallyConstrainedBasisConfig>("basis");
213
- }
214
-
215
- // returns the instance of {ConstrainedBasis} class
216
- get Basis() {
217
- const basisData = this.basis;
218
-
219
- return new ConstrainedBasis({
220
- ...basisData,
221
- cell: this.Lattice.vectors,
222
- constraints: basisData.constraints || [],
223
- });
224
- }
225
-
226
- /**
227
- * High-level access to unique elements from material instead of basis.
228
- */
229
- get uniqueElements() {
230
- return this.Basis.uniqueElements;
231
- }
232
-
233
- get lattice(): LatticeSchema {
234
- return this.prop("lattice") as LatticeSchema;
235
- }
236
-
237
- set lattice(config: LatticeSchema) {
238
- const originalIsInCrystalUnits = this.Basis.isInCrystalUnits;
239
- const basis = this.Basis;
240
- basis.toCartesian();
241
-
242
- const newLattice = new Lattice(config);
243
- basis.cell = newLattice.vectors;
244
- if (originalIsInCrystalUnits) basis.toCrystal();
245
-
246
- // Preserve all properties from the original basis to ensure constraints are included
247
- const newBasisConfig = basis.toJSON();
248
- this.setProp("basis", newBasisConfig);
249
- this.setProp("lattice", config);
250
-
251
- this.unsetFileProps();
252
- }
253
-
254
- get Lattice(): Lattice {
255
- return new Lattice(this.lattice);
256
- }
257
-
258
- /**
259
- * Returns the inchi string from the derivedProperties for a non-periodic material, or throws an error if the
260
- * inchi cannot be found.
261
- * @returns {String}
262
- */
263
- getInchiStringForHash(): string {
264
- const inchi = this.getDerivedPropertyByName("inchi");
265
- if (inchi) {
266
- return (inchi as InChIRepresentationSchema).value;
267
- }
268
- throw new Error("Hash cannot be created. Missing InChI string in derivedProperties");
269
- }
270
-
271
- /**
272
- * Calculates hash from basis and lattice. Algorithm expects the following:
273
- * - asserts lattice units to be angstrom
274
- * - asserts basis units to be crystal
275
- * - asserts basis coordinates and lattice measurements are rounded to hash precision
276
- * - forms strings for lattice and basis
277
- * - creates MD5 hash from basisStr + latticeStr + salt
278
- * @param salt Salt for hashing, empty string by default.
279
- * @param isScaled Whether to scale the lattice parameter 'a' to 1.
280
- */
281
- calculateHash(salt = "", isScaled = false, bypassNonPeriodicCheck = false): string {
282
- let message;
283
- if (!this.isNonPeriodic || bypassNonPeriodicCheck) {
284
- message =
285
- this.Basis.hashString + "#" + this.Lattice.getHashString(isScaled) + "#" + salt;
286
- } else {
287
- message = this.getInchiStringForHash();
288
- }
289
- return CryptoJS.MD5(message).toString();
290
- }
291
-
292
- set hash(hash: string) {
293
- this.setProp("hash", hash);
294
- }
295
-
296
- get hash(): string {
297
- return this.prop("hash") as string;
298
- }
299
-
300
- /**
301
- * Calculates hash from basis and lattice as above + scales lattice properties to make lattice.a = 1
302
- */
303
- get scaledHash(): string {
304
- return this.calculateHash("", true);
305
- }
306
-
307
- get external() {
308
- return this.prop<MaterialSchema["external"]>("external");
309
- }
310
-
311
- set external(external: MaterialSchema["external"]) {
312
- this.setProp("external", external);
313
- }
314
-
315
- /**
316
- * Converts basis to crystal/fractional coordinates.
317
- */
318
- toCrystal() {
319
- const basis = this.Basis;
320
- basis.toCrystal();
321
- this.setProp("basis", basis.toJSON());
322
- }
323
-
324
- /**
325
- * Converts current material's basis coordinates to cartesian.
326
- * No changes if coordinates already cartesian.
327
- */
328
- toCartesian() {
329
- const basis = this.Basis;
330
- basis.toCartesian();
331
- this.setProp("basis", basis.toJSON());
332
- }
333
-
334
- /**
335
- * Returns material's basis in XYZ format.
336
- */
337
- getBasisAsXyz(fractional = false): string {
338
- return parsers.xyz.fromMaterial(this.toJSON(), fractional);
339
- }
340
-
341
- /**
342
- * Returns material in Quantum Espresso output format:
343
- * ```
344
- * CELL_PARAMETERS (angstroms)
345
- * -0.543131284 -0.000000000 0.543131284
346
- * -0.000000000 0.543131284 0.543131284
347
- * -0.543131284 0.543131284 0.000000000
348
- *
349
- * ATOMIC_POSITIONS (crystal)
350
- * Si 0.000000000 0.000000000 -0.000000000
351
- * Si 0.250000000 0.250000000 0.250000000
352
- * ```
353
- */
354
- getAsQEFormat(): string {
355
- return parsers.espresso.toEspressoFormat(this.toJSON());
356
- }
357
-
358
- /**
359
- * Returns material in POSCAR format. Pass `true` to ignore original poscar source and re-serialize.
360
- */
361
- getAsPOSCAR(ignoreOriginal = false, omitConstraints = false): string {
362
- // By default return original source if exists
363
- if (this.src?.extension === "poscar" && !ignoreOriginal) {
364
- return this.src.text;
365
- }
366
- return parsers.poscar.toPoscar(this.toJSON(), omitConstraints);
367
- }
368
-
369
- /**
370
- * Returns a copy of the material with conventional cell constructed instead of primitive.
371
- */
372
- getACopyWithConventionalCell(): this {
373
- const material = this.clone();
374
-
375
- // if conventional and primitive cells are the same => return a copy.
376
- if (isConventionalCellSameAsPrimitiveForLatticeType(this.Lattice.type)) {
377
- return material;
378
- }
379
-
380
- const conventionalSupercellMatrix =
381
- PRIMITIVE_TO_CONVENTIONAL_CELL_MULTIPLIERS[this.Lattice.type];
382
- const conventionalLatticeType =
383
- PRIMITIVE_TO_CONVENTIONAL_CELL_LATTICE_TYPES[this.Lattice.type];
384
- const config = supercellTools.generateConfig(this, conventionalSupercellMatrix);
385
-
386
- config.lattice.type = conventionalLatticeType;
387
- config.name = `${this.name} - conventional cell`;
388
-
389
- // @ts-expect-error
390
- return new this.constructor(config) as this;
391
- }
392
-
393
- /**
394
- * @summary a series of checks for the material and returns an array of results in ConsistencyChecks format.
395
- * @returns Array of checks results
396
- */
397
- getConsistencyChecks(): ConsistencyCheck[] {
398
- const basisChecks = this.getBasisConsistencyChecks();
399
-
400
- // any other Material checks can be added here
401
-
402
- return basisChecks;
403
- }
404
-
405
- /**
406
- * @summary a series of checks for the material's basis and returns an array of results in ConsistencyChecks format.
407
- * @returns Array of checks results
408
- */
409
- getBasisConsistencyChecks(): ConsistencyCheck[] {
410
- const checks: ConsistencyCheck[] = [];
411
- const limit = 1000;
412
- const basis = this.Basis;
413
-
414
- if (this.Basis.elements.length < limit) {
415
- const overlappingAtomsGroups = basis.getOverlappingAtoms();
416
- overlappingAtomsGroups.forEach(({ id1, id2, element1, element2 }) => {
417
- checks.push(
418
- {
419
- key: `basis.coordinates.${id1}`,
420
- name: "atomsOverlap",
421
- severity: "warning",
422
- message: `Atom ${element1} is too close to ${element2} at position ${
423
- id2 + 1
424
- }`,
425
- },
426
- {
427
- key: `basis.coordinates.${id2}`,
428
- name: "atomsOverlap",
429
- severity: "warning",
430
- message: `Atom ${element2} is too close to ${element1} at position ${
431
- id1 + 1
432
- }`,
433
- },
434
- );
435
- });
436
- }
437
-
438
- return checks;
439
- }
440
-
441
- toJSON(): MaterialSchema & AnyObject {
442
- // validation intoJSON() method will fail if name is not set in _json
443
- this.name = this.name || this.formula;
444
-
445
- return {
446
- ...(super.toJSON() as MaterialSchema & AnyObject),
447
- lattice: this.Lattice.toJSON(),
448
- basis: this.Basis.toJSON(),
449
- isNonPeriodic: this.isNonPeriodic,
450
- };
451
- }
452
- }
453
-
454
- defaultableEntityMixin(Material);
455
- hasConsistencyChecksMixin(Material.prototype);
456
- hasMetadataMixin(Material.prototype);
457
-
458
- export { Material };
@@ -1,42 +0,0 @@
1
- import { expect } from "chai";
2
-
3
- import { Material } from "../../src/js/material";
4
- import { FeOStandata, Graphene, Na4Cl4, Silicon } from "./fixtures";
5
-
6
- const newBasisXYZ = `Si 0.000000 0.000000 0.000000
7
- Ge 0.250000 0.250000 0.250000
8
- `;
9
-
10
- describe("Material", () => {
11
- it("should return unique elements", () => {
12
- const material = new Material(Na4Cl4);
13
- expect(material.uniqueElements).to.have.same.members(["Na", "Cl"]);
14
- });
15
-
16
- it("should return cloned material", () => {
17
- const material = new Material(Silicon);
18
- const clonedMaterial = material.clone();
19
- clonedMaterial.setBasis(newBasisXYZ, "xyz", clonedMaterial.Basis.units);
20
- expect(clonedMaterial.Basis.elements).to.have.lengthOf(2);
21
- });
22
-
23
- describe("calculateHash", () => {
24
- [
25
- { name: "Silicon", fixture: Silicon },
26
- { name: "Graphene", fixture: Graphene },
27
- ].forEach(({ name, fixture }) => {
28
- it(`should match expected hash for ${name}`, () => {
29
- const material = new Material(fixture);
30
- expect(material.calculateHash()).to.equal((fixture as any).hash);
31
- expect(material.scaledHash).to.equal((fixture as any).scaledHash);
32
- });
33
- });
34
-
35
- it("should match expected hash for FeO (standata)", () => {
36
- const expectedHashes = require("../../fixtures/hashes.json");
37
- const material = new Material(FeOStandata);
38
- expect(material.calculateHash()).to.equal(expectedHashes.FeO.hash);
39
- expect(material.scaledHash).to.equal(expectedHashes.FeO.scaledHash);
40
- });
41
- });
42
- });