-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmaterial.js
310 lines (274 loc) · 8.71 KB
/
material.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
import { HasMetadataNamedDefaultableInMemoryEntity } from "@exabyte-io/code.js/dist/entity";
import CryptoJS from "crypto-js";
import lodash from "lodash";
import { ConstrainedBasis } from "./basis/constrained_basis";
import {
isConventionalCellSameAsPrimitiveForLatticeType,
PRIMITIVE_TO_CONVENTIONAL_CELL_LATTICE_TYPES,
PRIMITIVE_TO_CONVENTIONAL_CELL_MULTIPLIERS,
} from "./cell/conventional_cell";
import { ATOMIC_COORD_UNITS, units } from "./constants";
import { Lattice } from "./lattice/lattice";
import { LATTICE_TYPE } from "./lattice/types";
import parsers from "./parsers/parsers";
import supercellTools from "./tools/supercell";
const defaultMaterialConfig = {
name: "Silicon FCC",
basis: {
elements: [
{
id: 1,
value: "Si",
},
{
id: 2,
value: "Si",
},
],
coordinates: [
{
id: 1,
value: [0.0, 0.0, 0.0],
},
{
id: 2,
value: [0.25, 0.25, 0.25],
},
],
units: ATOMIC_COORD_UNITS.crystal,
},
lattice: {
// Primitive cell for Diamond FCC Silicon at ambient conditions
type: LATTICE_TYPE.FCC,
a: 3.867,
b: 3.867,
c: 3.867,
alpha: 60,
beta: 60,
gamma: 60,
units: {
length: units.angstrom,
angle: units.degree,
},
},
};
export class Material extends HasMetadataNamedDefaultableInMemoryEntity {
constructor(config) {
super(config);
this._json = lodash.cloneDeep(config || {});
}
toJSON() {
return this.toJSONDefault();
}
toJSONDefault() {
return {
lattice: this.Lattice.toJSON(),
basis: this.Basis.toJSON(),
name: this.name || this.formula,
isNonPeriodic: this.isNonPeriodic || false,
};
}
get name() {
return super.name || this.formula;
}
static get defaultConfig() {
return defaultMaterialConfig;
}
updateFormula() {
this.setProp("formula", this.Basis.formula);
this.setProp("unitCellFormula", this.Basis.unitCellFormula);
}
/**
* Gets Bolean value for whether or not a material is non-periodic vs periodic.
* False = periodic, True = non-periodic
*/
get isNonPeriodic() {
return this.prop("isNonPeriodic", false, true);
}
/**
* @summary Sets the value of isNonPeriodic based on Boolean value passed as an argument.
* @param {Boolean} bool
*/
set isNonPeriodic(bool) {
this.setProp("isNonPeriodic", bool);
}
/**
* @summary Returns the specific derived property (as specified by name) for a material.
* @param {String} name
* @returns {Object}
*/
getDerivedPropertyByName(name) {
return this.getDerivedProperties().find((x) => x.name === name);
}
/**
* @summary Returns the derived properties array for a material.
* @returns {Array}
*/
getDerivedProperties() {
return this.prop("derivedProperties", []);
}
/**
* Gets material's formula
*/
get formula() {
return this.prop("formula") || this.Basis.formula;
}
get unitCellFormula() {
return this.prop("unitCellFormula") || this.Basis.unitCellFormula;
}
/**
* @param textOrObject {String} Basis text or JSON object.
* @param format {String} Format (xyz, etc.)
* @param unitz {String} crystal/cartesian
*/
setBasis(textOrObject, format, unitz) {
let basis;
switch (format) {
case "xyz":
basis = parsers.xyz.toBasisConfig(textOrObject, unitz);
break;
default:
basis = textOrObject;
}
this.setProp("basis", basis);
this.updateFormula();
}
setBasisConstraints(constraints) {
this.setBasis({
...this.basis,
constraints,
});
}
get basis() {
return this.prop("basis", undefined, true);
}
// returns the instance of {ConstrainedBasis} class
get Basis() {
return new ConstrainedBasis({
...this.basis,
cell: this.Lattice.vectorArrays,
});
}
get lattice() {
return this.prop("lattice", undefined, true);
}
set lattice(config) {
this.setProp("lattice", config);
}
// returns the instance of {Lattice} class
get Lattice() {
return new Lattice(this.lattice);
}
/**
* Returns the inchi string from the derivedProperties for a non-periodic material, or throws an error if the
* inchi cannot be found.
* @returns {String}
*/
getInchiStringForHash() {
const inchi = this.getDerivedPropertyByName("inchi");
if (inchi) {
return inchi.value;
}
throw new Error("Hash cannot be created. Missing InChI string in derivedProperties");
}
/**
* Calculates hash from basis and lattice. Algorithm expects the following:
* - asserts lattice units to be angstrom
* - asserts basis units to be crystal
* - asserts basis coordinates and lattice measurements are rounded to hash precision
* - forms strings for lattice and basis
* - creates MD5 hash from basisStr + latticeStr + salt
* @param salt {String} Salt for hashing, empty string by default.
* @param isScaled {Boolean} Whether to scale the lattice parameter 'a' to 1.
*/
calculateHash(salt = "", isScaled = false) {
let message;
if (!this.isNonPeriodic) {
message =
this.Basis.hashString + "#" + this.Lattice.getHashString(isScaled) + "#" + salt;
} else {
message = this.getInchiStringForHash();
}
return CryptoJS.MD5(message).toString();
}
set hash(hash) {
this.setProp("hash", hash);
}
get hash() {
return this.prop("hash");
}
/**
* Calculates hash from basis and lattice as above + scales lattice properties to make lattice.a = 1
*/
get scaledHash() {
return this.calculateHash("", true);
}
/**
* Converts basis to crystal/fractional coordinates.
*/
toCrystal() {
const basis = this.Basis;
basis.toCrystal();
this.setProp("basis", basis.toJSON());
}
/**
* Converts current material's basis coordinates to cartesian.
* No changes if coordinates already cartesian.
*/
toCartesian() {
const basis = this.Basis;
basis.toCartesian();
this.setProp("basis", basis.toJSON());
}
/**
* Returns material's basis in XYZ format.
* @return {String}
*/
getBasisAsXyz(fractional = false) {
return parsers.xyz.fromMaterial(this.toJSON(), fractional);
}
/**
* Returns material in Quantum Espresso output format:
* ```
* CELL_PARAMETERS (angstroms)
* -0.543131284 -0.000000000 0.543131284
* -0.000000000 0.543131284 0.543131284
* -0.543131284 0.543131284 0.000000000
*
* ATOMIC_POSITIONS (crystal)
* Si 0.000000000 0.000000000 -0.000000000
* Si 0.250000000 0.250000000 0.250000000
* ```
* @return {String}
*/
getAsQEFormat() {
return parsers.espresso.toEspressoFormat(this.toJSON());
}
/**
* Returns material in POSCAR format. Pass `true` to ignore original poscar source and re-serialize.
*/
getAsPOSCAR(ignoreOriginal = false, omitConstraints = false) {
const { src } = this;
// By default return original source if exists
if (src && src.extension === "poscar" && !ignoreOriginal) {
return this.src.text;
}
return parsers.poscar.toPoscar(this.toJSON(), omitConstraints);
}
/**
* Returns a copy of the material with conventional cell constructed instead of primitive.
*/
getACopyWithConventionalCell() {
const material = this.clone();
// if conventional and primitive cells are the same => return a copy.
if (isConventionalCellSameAsPrimitiveForLatticeType(this.Lattice.type)) return material;
const conventionalSupercellMatrix =
PRIMITIVE_TO_CONVENTIONAL_CELL_MULTIPLIERS[this.Lattice.type];
const conventionalLatticeType =
PRIMITIVE_TO_CONVENTIONAL_CELL_LATTICE_TYPES[this.Lattice.type];
const config = supercellTools.generateConfig(material, conventionalSupercellMatrix, 1);
config.lattice.type = conventionalLatticeType;
config.name = `${material.name} - conventional cell`;
return new this.constructor(config);
}
}