matsci-parse 0.0.1
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/README.md +1 -0
- package/dist/main.js +254 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
A library to unify and interconvert between common material science files types.
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
class S {
|
|
2
|
+
constructor(e, t, o = {}) {
|
|
3
|
+
this.speciesIndex = e, this.cart = t, this.props = o;
|
|
4
|
+
}
|
|
5
|
+
getProp(e) {
|
|
6
|
+
return this.props[e];
|
|
7
|
+
}
|
|
8
|
+
setProp(e, t) {
|
|
9
|
+
this.props[e] = t;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function I(n) {
|
|
13
|
+
return n.sites.some((e) => Array.isArray(e.props.selectiveDynamics));
|
|
14
|
+
}
|
|
15
|
+
class x {
|
|
16
|
+
constructor({
|
|
17
|
+
lattice: e,
|
|
18
|
+
species: t,
|
|
19
|
+
sites: o
|
|
20
|
+
}) {
|
|
21
|
+
this.lattice = this._validateLattice(e), this.species = this._validateSpecies(t), this.sites = this._validateSites(o);
|
|
22
|
+
}
|
|
23
|
+
// ---------- Simple methods ----------
|
|
24
|
+
get numSites() {
|
|
25
|
+
return this.sites.length;
|
|
26
|
+
}
|
|
27
|
+
site(e) {
|
|
28
|
+
return this.sites[e];
|
|
29
|
+
}
|
|
30
|
+
cartCoords(e) {
|
|
31
|
+
return this.sites[e].cart;
|
|
32
|
+
}
|
|
33
|
+
siteSpecies(e) {
|
|
34
|
+
return this.species[this.sites[e].speciesIndex];
|
|
35
|
+
}
|
|
36
|
+
get elements() {
|
|
37
|
+
return this.species;
|
|
38
|
+
}
|
|
39
|
+
addSite(e, t) {
|
|
40
|
+
this._validateSpeciesIndex(e), this.sites.push(new S(e, t));
|
|
41
|
+
}
|
|
42
|
+
removeSite(e) {
|
|
43
|
+
this.sites.splice(e, 1);
|
|
44
|
+
}
|
|
45
|
+
// ---------- Internal validation ----------
|
|
46
|
+
_validateLattice(e) {
|
|
47
|
+
if (!Array.isArray(e) || e.length !== 3)
|
|
48
|
+
throw new Error("Lattice must be a 3x3 matrix");
|
|
49
|
+
return e.forEach((t) => {
|
|
50
|
+
if (!Array.isArray(t) || t.length !== 3)
|
|
51
|
+
throw new Error("Lattice vectors must be length 3");
|
|
52
|
+
}), e;
|
|
53
|
+
}
|
|
54
|
+
_validateSpecies(e) {
|
|
55
|
+
if (!Array.isArray(e)) throw new Error("Species must be an array");
|
|
56
|
+
return e.forEach((t) => {
|
|
57
|
+
if (typeof t != "string")
|
|
58
|
+
throw new Error("Species entries must be strings");
|
|
59
|
+
}), e;
|
|
60
|
+
}
|
|
61
|
+
_validateSites(e) {
|
|
62
|
+
if (!Array.isArray(e)) throw new Error("Sites must be an array");
|
|
63
|
+
return e.map((t) => {
|
|
64
|
+
if (t instanceof S) return t;
|
|
65
|
+
if (typeof t == "object" && typeof t.speciesIndex == "number" && Array.isArray(t.cart) && t.cart.length === 3)
|
|
66
|
+
return this._validateSpeciesIndex(t.speciesIndex), new S(
|
|
67
|
+
t.speciesIndex,
|
|
68
|
+
t.cart,
|
|
69
|
+
t.props ?? {}
|
|
70
|
+
);
|
|
71
|
+
throw new Error(
|
|
72
|
+
"Each site must be a Site instance or a valid site object"
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
_validateSpeciesIndex(e) {
|
|
77
|
+
if (e < 0 || e >= this.species.length)
|
|
78
|
+
throw new Error("speciesIndex out of bounds");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function A(n) {
|
|
82
|
+
const e = [], t = I(n);
|
|
83
|
+
e.push("Generated by CrystalStructure"), e.push("1.0"), n.lattice.forEach((s) => {
|
|
84
|
+
e.push(s.map((p) => p.toFixed(10)).join(" "));
|
|
85
|
+
});
|
|
86
|
+
const o = Array.from(
|
|
87
|
+
new Set(n.sites.map((s) => n.species[s.speciesIndex]))
|
|
88
|
+
), i = o.map(
|
|
89
|
+
(s) => n.sites.filter((p) => n.species[p.speciesIndex] === s).length
|
|
90
|
+
);
|
|
91
|
+
return e.push(o.join(" ")), e.push(i.join(" ")), t && e.push("Selective dynamics"), e.push("Cartesian"), o.forEach((s) => {
|
|
92
|
+
n.sites.filter((p) => n.species[p.speciesIndex] === s).forEach((p) => {
|
|
93
|
+
const a = p.cart.map((h) => h.toFixed(10)).join(" ");
|
|
94
|
+
if (t) {
|
|
95
|
+
const d = (Array.isArray(p.props?.selectiveDynamics) ? p.props.selectiveDynamics : [!0, !0, !0]).map((u) => u ? "T" : "F").join(" ");
|
|
96
|
+
e.push(`${a} ${d}`);
|
|
97
|
+
} else
|
|
98
|
+
e.push(a);
|
|
99
|
+
});
|
|
100
|
+
}), e.join(`
|
|
101
|
+
`);
|
|
102
|
+
}
|
|
103
|
+
function j(n) {
|
|
104
|
+
const e = n.trim().split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
105
|
+
let t = 0;
|
|
106
|
+
t++;
|
|
107
|
+
const o = parseFloat(e[t++]), i = [];
|
|
108
|
+
for (let l = 0; l < 3; l++)
|
|
109
|
+
i.push(
|
|
110
|
+
e[t++].split(/\s+/).map((y) => parseFloat(y) * o)
|
|
111
|
+
);
|
|
112
|
+
let s = e[t].trim(), p;
|
|
113
|
+
/^[A-Za-z]/.test(s) && (p = s.split(/\s+/), t++);
|
|
114
|
+
const a = e[t++].split(/\s+/).map(Number);
|
|
115
|
+
p || (p = a.map((l, y) => `X${y + 1}`));
|
|
116
|
+
let h = !1;
|
|
117
|
+
e[t]?.toLowerCase().startsWith("s") && (h = !0, t++);
|
|
118
|
+
const u = e[t++].toLowerCase().startsWith("d"), m = [];
|
|
119
|
+
return p.forEach((l, y) => {
|
|
120
|
+
for (let r = 0; r < a[y]; r++) {
|
|
121
|
+
const c = e[t++].split(/\s+/).map(Number);
|
|
122
|
+
let f;
|
|
123
|
+
u ? f = [
|
|
124
|
+
c[0] * i[0][0] + c[1] * i[1][0] + c[2] * i[2][0],
|
|
125
|
+
c[0] * i[0][1] + c[1] * i[1][1] + c[2] * i[2][1],
|
|
126
|
+
c[0] * i[0][2] + c[1] * i[1][2] + c[2] * i[2][2]
|
|
127
|
+
] : f = c.slice(0, 3);
|
|
128
|
+
let w = {};
|
|
129
|
+
if (h) {
|
|
130
|
+
const g = e[t - 1].split(/\s+/).slice(3, 6).map((v) => v.toUpperCase() === "T");
|
|
131
|
+
g.length === 3 && (w.selectiveDynamics = g);
|
|
132
|
+
}
|
|
133
|
+
m.push(new S(y, f, w));
|
|
134
|
+
}
|
|
135
|
+
}), new x({
|
|
136
|
+
lattice: i,
|
|
137
|
+
species: p,
|
|
138
|
+
sites: m
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function C(n) {
|
|
142
|
+
const e = [];
|
|
143
|
+
return e.push("CRYSTAL"), e.push("PRIMVEC"), n.lattice.forEach((t) => {
|
|
144
|
+
e.push(t.map((o) => o.toFixed(10)).join(" "));
|
|
145
|
+
}), e.push("PRIMCOORD"), e.push(`${n.numSites} 1`), n.sites.forEach((t) => {
|
|
146
|
+
const o = n.species[t.speciesIndex];
|
|
147
|
+
e.push(`${o} ${t.cart.map((i) => i.toFixed(10)).join(" ")}`);
|
|
148
|
+
}), e.join(`
|
|
149
|
+
`);
|
|
150
|
+
}
|
|
151
|
+
function D(n) {
|
|
152
|
+
const e = n.trim().split(/\r?\n/).map((a) => a.trim()).filter((a) => a.length > 0), t = [], o = [], i = [];
|
|
153
|
+
let s = 0;
|
|
154
|
+
for (; s < e.length && e[s].toUpperCase() !== "PRIMVEC"; ) s++;
|
|
155
|
+
if (s === e.length) throw new Error("PRIMVEC block not found in XSF");
|
|
156
|
+
s++;
|
|
157
|
+
for (let a = 0; a < 3; a++)
|
|
158
|
+
t.push(e[s++].split(/\s+/).map(Number));
|
|
159
|
+
for (; s < e.length && e[s].toUpperCase() !== "PRIMCOORD"; ) s++;
|
|
160
|
+
if (s === e.length) throw new Error("PRIMCOORD block not found in XSF");
|
|
161
|
+
s++;
|
|
162
|
+
const [p] = e[s++].split(/\s+/).map(Number);
|
|
163
|
+
for (let a = 0; a < p; a++) {
|
|
164
|
+
const h = e[s++].split(/\s+/), d = h[0], u = h.slice(1, 4).map(Number);
|
|
165
|
+
let m = o.indexOf(d);
|
|
166
|
+
m === -1 && (o.push(d), m = o.length - 1), i.push(new S(m, u));
|
|
167
|
+
}
|
|
168
|
+
return new x({ lattice: t, species: o, sites: i });
|
|
169
|
+
}
|
|
170
|
+
function F(n) {
|
|
171
|
+
const e = [], t = n.sites.length;
|
|
172
|
+
e.push(String(t));
|
|
173
|
+
const o = I(n), i = n.lattice, s = [
|
|
174
|
+
i[0][0],
|
|
175
|
+
i[1][0],
|
|
176
|
+
i[2][0],
|
|
177
|
+
i[0][1],
|
|
178
|
+
i[1][1],
|
|
179
|
+
i[2][1],
|
|
180
|
+
i[0][2],
|
|
181
|
+
i[1][2],
|
|
182
|
+
i[2][2]
|
|
183
|
+
].join(" "), p = [
|
|
184
|
+
"species:S:1",
|
|
185
|
+
"pos:R:3",
|
|
186
|
+
o ? "selectiveDynamics:L:3" : null
|
|
187
|
+
].filter(Boolean).join(":");
|
|
188
|
+
e.push(`Lattice="${s}" Properties=${p}`);
|
|
189
|
+
for (const a of n.sites) {
|
|
190
|
+
const h = n.species[a.speciesIndex], [d, u, m] = a.cart;
|
|
191
|
+
let l = `${h} ${d.toFixed(10)} ${u.toFixed(10)} ${m.toFixed(10)}`;
|
|
192
|
+
if (o) {
|
|
193
|
+
const y = Array.isArray(a.props?.selectiveDynamics) ? a.props.selectiveDynamics : [!0, !0, !0];
|
|
194
|
+
l += " " + y.map((r) => r ? "T" : "F").join(" ");
|
|
195
|
+
}
|
|
196
|
+
e.push(l);
|
|
197
|
+
}
|
|
198
|
+
return e.join(`
|
|
199
|
+
`);
|
|
200
|
+
}
|
|
201
|
+
function L(n) {
|
|
202
|
+
const e = n.trim().split(/\r?\n/).map((r) => r.trim()).filter((r) => r.length > 0);
|
|
203
|
+
let t = 0;
|
|
204
|
+
const o = parseInt(e[t++], 10);
|
|
205
|
+
if (!Number.isFinite(o))
|
|
206
|
+
throw new Error("Invalid XYZ: first line must be atom count");
|
|
207
|
+
const i = e[t++], s = {}, p = /(\w+)=(".*?"|\S+)/g;
|
|
208
|
+
for (const r of i.matchAll(p)) {
|
|
209
|
+
let c = r[2];
|
|
210
|
+
c.startsWith('"') && (c = c.slice(1, -1)), s[r[1]] = c;
|
|
211
|
+
}
|
|
212
|
+
let a;
|
|
213
|
+
if (s.Lattice) {
|
|
214
|
+
const r = s.Lattice.split(/\s+/).map(Number);
|
|
215
|
+
if (r.length !== 9) throw new Error("Invalid Lattice in extended XYZ");
|
|
216
|
+
a = [
|
|
217
|
+
[r[0], r[3], r[6]],
|
|
218
|
+
[r[1], r[4], r[7]],
|
|
219
|
+
[r[2], r[5], r[8]]
|
|
220
|
+
];
|
|
221
|
+
} else
|
|
222
|
+
throw new Error("Lattice must be present in extended XYZ format");
|
|
223
|
+
let h = 0, d = 1, u = null;
|
|
224
|
+
if (s.Properties) {
|
|
225
|
+
const r = s.Properties.split(":");
|
|
226
|
+
let c = 0;
|
|
227
|
+
for (let f = 0; f < r.length; f += 3) {
|
|
228
|
+
const w = r[f], g = parseInt(r[f + 2], 10);
|
|
229
|
+
w === "species" && (h = c), w === "pos" && (d = c), w === "selectiveDynamics" && (u = c), c += g;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const m = [], l = /* @__PURE__ */ new Map(), y = [];
|
|
233
|
+
for (let r = 0; r < o; r++) {
|
|
234
|
+
const c = e[t++].split(/\s+/), f = c[h];
|
|
235
|
+
l.has(f) || (l.set(f, m.length), m.push(f));
|
|
236
|
+
const w = l.get(f), g = c.slice(d, d + 3).map(Number), v = {};
|
|
237
|
+
if (u !== null) {
|
|
238
|
+
const E = c.slice(u, u + 3).map((b) => b.toUpperCase() === "T");
|
|
239
|
+
E.length === 3 && (v.selectiveDynamics = E);
|
|
240
|
+
}
|
|
241
|
+
y.push(new S(w, g, v));
|
|
242
|
+
}
|
|
243
|
+
return new x({ lattice: a, species: m, sites: y });
|
|
244
|
+
}
|
|
245
|
+
export {
|
|
246
|
+
x as CrystalStructure,
|
|
247
|
+
S as Site,
|
|
248
|
+
j as poscarToStructure,
|
|
249
|
+
A as structureToPoscar,
|
|
250
|
+
C as structureToXsf,
|
|
251
|
+
F as structureToXyz,
|
|
252
|
+
D as xsfToStructure,
|
|
253
|
+
L as xyzToStructure
|
|
254
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "matsci-parse",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/main.js",
|
|
6
|
+
"module": "dist/main.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "vite",
|
|
12
|
+
"build": "vite build",
|
|
13
|
+
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
|
14
|
+
"test": "node --experimental-vm-modules node_modules/.bin/jest"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@eslint/js": "^9.39.2",
|
|
18
|
+
"@types/jest": "^30.0.0",
|
|
19
|
+
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
20
|
+
"@typescript-eslint/parser": "^8.54.0",
|
|
21
|
+
"eslint": "^9.39.2",
|
|
22
|
+
"globals": "^17.2.0",
|
|
23
|
+
"jest": "^30.2.0",
|
|
24
|
+
"ts-jest": "^29.4.6",
|
|
25
|
+
"ts-node": "^10.9.2",
|
|
26
|
+
"typescript": "^5.9.3",
|
|
27
|
+
"vite": "^7.3.1"
|
|
28
|
+
}
|
|
29
|
+
}
|