@ccp-nc/crystvis-js 0.4.13
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/.eslintrc.json +16 -0
- package/.github/workflows/test-mocha.yml +30 -0
- package/.vscode/settings.json +4 -0
- package/LICENSE +21 -0
- package/README.html +1127 -0
- package/README.md +76 -0
- package/demo/demo.css +30 -0
- package/demo/index.html +76 -0
- package/demo/main.js +143 -0
- package/docs/.nojekyll +0 -0
- package/docs-tutorials/Events.md +57 -0
- package/docs-tutorials/Queries.md +50 -0
- package/fonts/Rubik/OFL.txt +93 -0
- package/fonts/Rubik/README.txt +77 -0
- package/fonts/Rubik/Rubik-Italic-VariableFont_wght.ttf +0 -0
- package/fonts/Rubik/Rubik-VariableFont_wght.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Black.ttf +0 -0
- package/fonts/Rubik/static/Rubik-BlackItalic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Bold.ttf +0 -0
- package/fonts/Rubik/static/Rubik-BoldItalic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-ExtraBold.ttf +0 -0
- package/fonts/Rubik/static/Rubik-ExtraBoldItalic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Italic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Light.ttf +0 -0
- package/fonts/Rubik/static/Rubik-LightItalic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Medium.ttf +0 -0
- package/fonts/Rubik/static/Rubik-MediumItalic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Regular.ttf +0 -0
- package/fonts/Rubik/static/Rubik-SemiBold.ttf +0 -0
- package/fonts/Rubik/static/Rubik-SemiBoldItalic.ttf +0 -0
- package/index.html +25 -0
- package/index.js +11 -0
- package/jsconf.json +14 -0
- package/lib/assets/fonts/Rubik-Medium.fnt +297 -0
- package/lib/assets/fonts/Rubik-Medium.png +0 -0
- package/lib/assets/fonts/bmpfonts.in.js +16 -0
- package/lib/assets/fonts/bmpfonts.js +9 -0
- package/lib/assets/fonts/font.js +82 -0
- package/lib/assets/fonts/index.js +14 -0
- package/lib/assets/fonts/threebmfont.js +28 -0
- package/lib/data.js +125 -0
- package/lib/formats/cell.js +114 -0
- package/lib/formats/cif.js +22 -0
- package/lib/formats/magres.js +337 -0
- package/lib/formats/xyz.js +124 -0
- package/lib/loader.js +87 -0
- package/lib/model.js +2076 -0
- package/lib/modelview.js +382 -0
- package/lib/nmrdata.js +2898 -0
- package/lib/orbit.js +1233 -0
- package/lib/primitives/atoms.js +261 -0
- package/lib/primitives/cell.js +160 -0
- package/lib/primitives/dither.js +156 -0
- package/lib/primitives/ellipsoid.js +183 -0
- package/lib/primitives/geometries.js +20 -0
- package/lib/primitives/index.js +48 -0
- package/lib/primitives/isosurface.js +171 -0
- package/lib/primitives/shapes.js +100 -0
- package/lib/primitives/sprites.js +172 -0
- package/lib/query.js +158 -0
- package/lib/render.js +440 -0
- package/lib/selbox.js +361 -0
- package/lib/shaders/aura.frag +26 -0
- package/lib/shaders/aura.vert +37 -0
- package/lib/shaders/dither.frag +42 -0
- package/lib/shaders/dither.vert +8 -0
- package/lib/shaders/index.in.js +17 -0
- package/lib/shaders/index.js +25 -0
- package/lib/shaders/msdf300.frag +25 -0
- package/lib/shaders/msdf300.vert +45 -0
- package/lib/tensor.js +227 -0
- package/lib/utils.js +168 -0
- package/lib/visualizer.js +480 -0
- package/package.json +106 -0
- package/scripts/build-bundle.js +17 -0
- package/scripts/build-fonts.js +43 -0
- package/scripts/build-resources.js +46 -0
- package/scripts/plugins-shim.js +10 -0
- package/test/chemdata.js +69 -0
- package/test/data/CHA.cif +74 -0
- package/test/data/H2O.xyz +8 -0
- package/test/data/H2_bound.xyz +4 -0
- package/test/data/ethanol.cell +25 -0
- package/test/data/ethanol.magres +238 -0
- package/test/data/example_single.cif +789 -0
- package/test/data/frac.cell +8 -0
- package/test/data/org.cif +427 -0
- package/test/data/pyridine.xyz +13 -0
- package/test/data/si8.xyz +10 -0
- package/test/loader.js +107 -0
- package/test/model.js +368 -0
- package/test/query.js +135 -0
- package/test/tensor.js +133 -0
- package/test/test-html/examples.js +1485 -0
- package/test/test-html/index.html +33 -0
- package/test/test-html/index.js +279 -0
- package/tools/compile_colors.py +120 -0
- package/tools/compile_periodic.py +96 -0
- package/tools/ptable.json +497 -0
- package/tools/test +5844 -0
package/test/model.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import chai from 'chai'
|
|
5
|
+
import chaiAlmost from 'chai-almost'
|
|
6
|
+
|
|
7
|
+
import _ from 'lodash';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import {
|
|
11
|
+
fileURLToPath
|
|
12
|
+
} from 'url';
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
Atoms as Atoms
|
|
16
|
+
} from 'crystcif-parse';
|
|
17
|
+
import {
|
|
18
|
+
Model,
|
|
19
|
+
AtomImage,
|
|
20
|
+
BondImage
|
|
21
|
+
} from '../lib/model.js';
|
|
22
|
+
import {
|
|
23
|
+
ModelView as ModelView
|
|
24
|
+
} from '../lib/modelview.js';
|
|
25
|
+
import {
|
|
26
|
+
Loader as Loader
|
|
27
|
+
} from '../lib/loader.js';
|
|
28
|
+
|
|
29
|
+
chai.use(chaiAlmost(1e-3));
|
|
30
|
+
|
|
31
|
+
const expect = chai.expect
|
|
32
|
+
const __dirname = path.dirname(fileURLToPath(
|
|
33
|
+
import.meta.url));
|
|
34
|
+
|
|
35
|
+
// Load test files
|
|
36
|
+
var cif = fs.readFileSync(path.join(__dirname, 'data', 'CHA.cif'), "utf8");
|
|
37
|
+
var cha = Atoms.readCif(cif)['CHA'];
|
|
38
|
+
var chamodel = new Model(cha);
|
|
39
|
+
var chamodel3 = new Model(cha, {
|
|
40
|
+
supercell: [3, 3, 3]
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
cif = fs.readFileSync(path.join(__dirname, 'data', 'org.cif'), "utf8");
|
|
44
|
+
var org = Atoms.readCif(cif)['1501936'];
|
|
45
|
+
var orgmodel = new Model(org);
|
|
46
|
+
|
|
47
|
+
var xyz = fs.readFileSync(path.join(__dirname, 'data', 'pyridine.xyz'), "utf8");
|
|
48
|
+
var loader = new Loader();
|
|
49
|
+
var pyr = loader.load(xyz, 'xyz')['xyz'];
|
|
50
|
+
var pyrmodel = new Model(pyr);
|
|
51
|
+
|
|
52
|
+
xyz = fs.readFileSync(path.join(__dirname, 'data', 'si8.xyz'), "utf8");
|
|
53
|
+
var si = loader.load(xyz, 'xyz')['xyz'];
|
|
54
|
+
var simodel = new Model(si);
|
|
55
|
+
var simodel3 = new Model(si, {
|
|
56
|
+
supercell: [3, 3, 3]
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
xyz = fs.readFileSync(path.join(__dirname, 'data', 'H2O.xyz'), "utf8");
|
|
60
|
+
var h2o = loader.load(xyz, 'xyz')['xyz'];
|
|
61
|
+
var h2omodel = new Model(h2o);
|
|
62
|
+
|
|
63
|
+
describe('#atomimage', function() {
|
|
64
|
+
it('should correctly compute the periodic copy position', function() {
|
|
65
|
+
var aim = new AtomImage(chamodel, 0, [1, 0, 0]);
|
|
66
|
+
[25.339775, 1.16060394, 1.8119109].forEach(function(v, i) {
|
|
67
|
+
expect(aim.xyz[i]).to.be.closeTo(v, 1e-5);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
aim = new AtomImage(chamodel, 0, [-1, 1, 1]);
|
|
71
|
+
[-8.847725, 13.00350134, 16.5789109].forEach(function(v, i) {
|
|
72
|
+
expect(aim.xyz[i]).to.be.closeTo(v, 1e-5);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
it('should correctly identify equalities', function() {
|
|
76
|
+
var ai0 = new AtomImage(chamodel, 0, [0, 0, 1]);
|
|
77
|
+
var ai1 = new AtomImage(chamodel, 0, [0, 0, 1]);
|
|
78
|
+
var ai2 = new AtomImage(chamodel, 0, [1, 0, 0]);
|
|
79
|
+
var ai3 = new AtomImage(simodel, 0, [0, 0, 1]);
|
|
80
|
+
|
|
81
|
+
expect(ai0.equals(ai1)).to.be.equal(true);
|
|
82
|
+
expect(ai0.equals(ai2)).to.be.equal(false);
|
|
83
|
+
expect(ai0.equals(ai3)).to.be.equal(false);
|
|
84
|
+
});
|
|
85
|
+
it('should correctly generate string IDs', function() {
|
|
86
|
+
|
|
87
|
+
var ai = new AtomImage(chamodel, 2, [3, -1, 2]);
|
|
88
|
+
|
|
89
|
+
expect(ai.id).to.equal('2_3_-1_2');
|
|
90
|
+
});
|
|
91
|
+
it('should correctly calculate its integer index', function() {
|
|
92
|
+
|
|
93
|
+
for (var i = 0; i < simodel3.atoms.length; ++i) {
|
|
94
|
+
var ai = simodel3.atoms[i];
|
|
95
|
+
expect(ai.imgIndex).to.equal(i);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
it('should correctly identify the closest bonding neighbours', function() {
|
|
99
|
+
|
|
100
|
+
// This one relies on Model to compute the right bonds
|
|
101
|
+
|
|
102
|
+
var atoms = h2omodel.atoms;
|
|
103
|
+
var a = atoms[0];
|
|
104
|
+
|
|
105
|
+
expect(a.bondedAtoms).to.deep.equal([atoms[1], atoms[2]]);
|
|
106
|
+
|
|
107
|
+
});
|
|
108
|
+
it('should correctly retrieve a corresponding array value', function() {
|
|
109
|
+
|
|
110
|
+
simodel.setArray('test_arr', _.range(simodel.length));
|
|
111
|
+
|
|
112
|
+
for (var i = 0; i < simodel.length; ++i) {
|
|
113
|
+
expect(simodel.atoms[i].getArrayValue('test_arr'), i);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('#bondimage', function() {
|
|
120
|
+
it('should correctly compute the distance between atoms in the bond', function() {
|
|
121
|
+
|
|
122
|
+
var a1 = new AtomImage(h2omodel, 0, [0, 0, 0]);
|
|
123
|
+
var a2 = new AtomImage(h2omodel, 1, [0, 0, 0]);
|
|
124
|
+
|
|
125
|
+
var b = new BondImage(h2omodel, a1, a2);
|
|
126
|
+
|
|
127
|
+
expect(b.length).to.almost.equal(0.9686);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('#model', function() {
|
|
132
|
+
|
|
133
|
+
it('should generate the right atomic labels', function() {
|
|
134
|
+
expect(h2omodel._labels).to.deep.equal(['O_1', 'H_1', 'H_2', 'O_2', 'H_3', 'H_4']);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should correctly compute a supercell grid', function() {
|
|
138
|
+
expect(chamodel3.supercellGrid.length).to.be.equal(27);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should correctly compute the minimum supercell for given radii', function() {
|
|
142
|
+
expect(orgmodel.minimumSupercell(5)).to.deep.equal([3, 3, 3]);
|
|
143
|
+
expect(orgmodel.minimumSupercell(10)).to.deep.equal([5, 5, 3]);
|
|
144
|
+
expect(orgmodel.minimumSupercell(20)).to.deep.equal([7, 7, 5]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should correctly return its various properties', function() {
|
|
148
|
+
expect(pyrmodel.length).to.equal(11);
|
|
149
|
+
expect(chamodel.periodic).to.be.true;
|
|
150
|
+
expect(pyrmodel.periodic).to.be.false;
|
|
151
|
+
expect(simodel.periodic).to.be.true;
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should behave gracefully in case of non-periodic systems', function() {
|
|
155
|
+
expect(pyrmodel.cell).to.be.null;
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should correctly query for atoms in various ways', function() {
|
|
159
|
+
// Here we only test the raw query functions, not meant for
|
|
160
|
+
// public use
|
|
161
|
+
|
|
162
|
+
var found = pyrmodel._queryElements(['C']);
|
|
163
|
+
expect(found).to.deep.equal([0, 1, 2, 4, 5]);
|
|
164
|
+
|
|
165
|
+
// Cell
|
|
166
|
+
found = chamodel._queryCell([5, 5, 5]); // Beyond the supercell size
|
|
167
|
+
expect(found).to.deep.equal([]);
|
|
168
|
+
|
|
169
|
+
found = chamodel3._queryCell([1, 1, 1]);
|
|
170
|
+
expect(found.length).to.equal(chamodel.length);
|
|
171
|
+
expect(found[0]).to.equal(26 * chamodel.length);
|
|
172
|
+
|
|
173
|
+
// Box
|
|
174
|
+
found = pyrmodel._queryBox([-1, -0.5, -2.3], [0, 0.5, 1.7]);
|
|
175
|
+
found.sort();
|
|
176
|
+
expect(found).to.deep.equal([0, 3, 6]);
|
|
177
|
+
|
|
178
|
+
found = simodel._queryBox([-1.5, -1.5, -1.5], [1.5, 1.5, 1.5]);
|
|
179
|
+
expect(found).to.deep.equal([0, 1]);
|
|
180
|
+
|
|
181
|
+
// Bigger supercell
|
|
182
|
+
found = simodel3._queryBox([-1.5, -1.5, -1.5], [1.5, 1.5, 1.5]);
|
|
183
|
+
expect(found).to.deep.equal([11, 29, 79, 104, 105]);
|
|
184
|
+
|
|
185
|
+
found = simodel3._querySphere([0, 0, 0], 2.4);
|
|
186
|
+
expect(found).to.deep.equal([11, 29, 79, 104, 105]);
|
|
187
|
+
|
|
188
|
+
// Indices
|
|
189
|
+
found = simodel3._queryIndices(0);
|
|
190
|
+
expect(found).to.deep.equal(_.range(27).map((x) => {
|
|
191
|
+
return 8*x;
|
|
192
|
+
}));
|
|
193
|
+
|
|
194
|
+
// Using an atom as the centre
|
|
195
|
+
found = simodel._querySphere(simodel.atoms[0], 2.4);
|
|
196
|
+
expect(found).to.deep.equal([0, 1]);
|
|
197
|
+
|
|
198
|
+
// Bonds
|
|
199
|
+
found = h2omodel._queryBonded(h2omodel.atoms[0]);
|
|
200
|
+
expect(found).to.deep.equal([1, 2]);
|
|
201
|
+
found = h2omodel._queryBonded(h2omodel.atoms[0], 2);
|
|
202
|
+
expect(found).to.deep.equal([1, 2]);
|
|
203
|
+
found = h2omodel._queryBonded(h2omodel.atoms[0], 2, true);
|
|
204
|
+
expect(found.length).to.equal(0);
|
|
205
|
+
found = pyrmodel._queryBonded(pyrmodel.atoms[3], 2, true);
|
|
206
|
+
found.sort();
|
|
207
|
+
expect(found).to.deep.equal([1, 5, 8, 9]);
|
|
208
|
+
|
|
209
|
+
// Molecules
|
|
210
|
+
found = h2omodel._queryMolecule(h2omodel.atoms[0]);
|
|
211
|
+
found.sort();
|
|
212
|
+
expect(found).to.deep.equal([0, 1, 2]);
|
|
213
|
+
found = h2omodel._queryMolecule(h2omodel.atoms[3]); // Out of bounds
|
|
214
|
+
expect(found).to.deep.equal([3]);
|
|
215
|
+
|
|
216
|
+
// Test a more complex query
|
|
217
|
+
found = simodel3.find({
|
|
218
|
+
'$and': [{
|
|
219
|
+
'box': [
|
|
220
|
+
[0, 0, 0],
|
|
221
|
+
[2, 2, 2]
|
|
222
|
+
]
|
|
223
|
+
}, {
|
|
224
|
+
'box': [
|
|
225
|
+
[1, 1, 1],
|
|
226
|
+
[3, 3, 3]
|
|
227
|
+
]
|
|
228
|
+
}]
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
expect(found.length).to.equal(1);
|
|
232
|
+
expect(found.atoms[0].index).to.equal(1);
|
|
233
|
+
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should identify the right bonds', function() {
|
|
237
|
+
|
|
238
|
+
var bonds = h2omodel._bondmat;
|
|
239
|
+
|
|
240
|
+
expect(bonds[0][1]).to.deep.equal([
|
|
241
|
+
[0, 0, 0]
|
|
242
|
+
]);
|
|
243
|
+
expect(bonds[0][2]).to.deep.equal([
|
|
244
|
+
[0, 0, 0]
|
|
245
|
+
]);
|
|
246
|
+
expect(bonds[3][4]).to.deep.equal([
|
|
247
|
+
[0, 0, -1]
|
|
248
|
+
]);
|
|
249
|
+
expect(bonds[3][5]).to.deep.equal([
|
|
250
|
+
[0, -1, -1]
|
|
251
|
+
]);
|
|
252
|
+
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should identify the right molecules', function() {
|
|
256
|
+
|
|
257
|
+
expect(h2omodel._molinds).to.deep.equal([0, 0, 0, 1, 1, 1]);
|
|
258
|
+
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('should correctly load a model as molecular crystal', function() {
|
|
262
|
+
|
|
263
|
+
var h2omolcryst = new Model(h2o, {
|
|
264
|
+
molecularCrystal: true
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
for (let i = 0; i < h2omolcryst._molecules.length; ++i) {
|
|
268
|
+
let mol = h2omolcryst._molecules[i];
|
|
269
|
+
for (let j = 0; j < mol.length; ++j) {
|
|
270
|
+
expect(mol[j].cell).to.deep.equal([0, 0, 0]);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Check that it didn't alter the original Atoms object
|
|
275
|
+
expect(h2omolcryst.positions).to.not.deep.equal(h2omodel.positions);
|
|
276
|
+
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should correctly work with isotopes', function() {
|
|
280
|
+
|
|
281
|
+
var h2omodel = new Model(h2o, {
|
|
282
|
+
supercell: [2,1,1]
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
var a0 = h2omodel.find({indices: 0}).atoms;
|
|
286
|
+
|
|
287
|
+
expect(a0[0].isotope).to.equal(16);
|
|
288
|
+
expect(a0[1].isotope).to.equal(16);
|
|
289
|
+
|
|
290
|
+
// Now set them both
|
|
291
|
+
a0[0].isotopeGlobal = 17;
|
|
292
|
+
expect(a0[0].isotope).to.equal(17);
|
|
293
|
+
expect(a0[1].isotope).to.equal(17);
|
|
294
|
+
|
|
295
|
+
// Now only one
|
|
296
|
+
a0[1].isotope = 18;
|
|
297
|
+
expect(a0[0].isotope).to.equal(17);
|
|
298
|
+
expect(a0[1].isotope).to.equal(18);
|
|
299
|
+
|
|
300
|
+
// And attached data
|
|
301
|
+
expect(a0[0].isotopeData.gamma/1e7).to.almost.equal(-3.628);
|
|
302
|
+
expect(a0[1].isotopeData.gamma).to.equal(null); // No spin
|
|
303
|
+
|
|
304
|
+
// Load NMR active isotopes
|
|
305
|
+
h2omodel = new Model(h2o, {
|
|
306
|
+
supercell: [1,1,1],
|
|
307
|
+
useNMRActiveIsotopes: true
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
expect(h2omodel.atoms[0].isotope).to.equal(17);
|
|
311
|
+
expect(h2omodel.atoms[1].isotope).to.equal(1);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe('#modelview', function() {
|
|
316
|
+
|
|
317
|
+
it('should correctly AND two successive queries', function() {
|
|
318
|
+
|
|
319
|
+
var mv1 = h2omodel.find({
|
|
320
|
+
'cell': [
|
|
321
|
+
[0, 0, 0]
|
|
322
|
+
]
|
|
323
|
+
});
|
|
324
|
+
var mv2 = mv1.find({
|
|
325
|
+
'elements': 'O'
|
|
326
|
+
});
|
|
327
|
+
expect(mv2.indices).to.deep.equal([0, 3]);
|
|
328
|
+
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should correctly perform boolean operations between views', function() {
|
|
332
|
+
|
|
333
|
+
var mv1 = h2omodel.find({
|
|
334
|
+
'elements': 'O'
|
|
335
|
+
});
|
|
336
|
+
var mv2 = h2omodel.find({
|
|
337
|
+
'elements': 'H'
|
|
338
|
+
});
|
|
339
|
+
var mv3 = h2omodel.find({
|
|
340
|
+
'sphere': [
|
|
341
|
+
[0, 0, 0], 1
|
|
342
|
+
]
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
var mvAnd = mv1.and(mv2);
|
|
346
|
+
expect(mvAnd.length).to.equal(0);
|
|
347
|
+
|
|
348
|
+
var mvOr = mv1.or(mv2);
|
|
349
|
+
expect(mvOr.indices.sort()).to.deep.equal([0, 1, 2, 3, 4, 5]);
|
|
350
|
+
|
|
351
|
+
var mvXor = mv1.xor(mv3);
|
|
352
|
+
expect(mvXor.indices.sort()).to.deep.equal([1, 2, 3]);
|
|
353
|
+
|
|
354
|
+
var mvNot = mv3.not();
|
|
355
|
+
expect(mvNot.indices.sort()).to.deep.equal([3, 4, 5]);
|
|
356
|
+
|
|
357
|
+
// Throw exception
|
|
358
|
+
var mvSi = simodel.find({
|
|
359
|
+
'all': []
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
expect(function() {
|
|
363
|
+
mv1.and(mvSi);
|
|
364
|
+
}).to.throw('The two ModelViews do not refer to the same Model');
|
|
365
|
+
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
});
|
package/test/query.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import chai from 'chai';
|
|
4
|
+
|
|
5
|
+
import _ from 'lodash'
|
|
6
|
+
import {
|
|
7
|
+
QueryParser as QueryParser
|
|
8
|
+
} from '../lib/query.js';
|
|
9
|
+
|
|
10
|
+
const expect = chai.expect;
|
|
11
|
+
|
|
12
|
+
// Create a test object for queries
|
|
13
|
+
var QuerySandbox = function(n) {
|
|
14
|
+
this._nums = _.range(0, n);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
QuerySandbox.prototype = {
|
|
18
|
+
even: function() {
|
|
19
|
+
return _.filter(this._nums, function(x) {
|
|
20
|
+
return x % 2 == 0
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
odd: function() {
|
|
24
|
+
return _.filter(this._nums, function(x) {
|
|
25
|
+
return x % 2 == 1
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
all: function() {
|
|
29
|
+
return _.cloneDeep(this._nums);
|
|
30
|
+
},
|
|
31
|
+
greater: function(val) {
|
|
32
|
+
return _.filter(this._nums, function(x) {
|
|
33
|
+
return x > val;
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
lesser: function(val) {
|
|
37
|
+
return _.filter(this._nums, function(x) {
|
|
38
|
+
return x < val;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
describe('#query', function() {
|
|
44
|
+
it('should correctly parse an individual query', function() {
|
|
45
|
+
var qs = new QuerySandbox(10);
|
|
46
|
+
var qp = new QueryParser({
|
|
47
|
+
'even': qs.even,
|
|
48
|
+
'odd': qs.odd
|
|
49
|
+
},
|
|
50
|
+
qs);
|
|
51
|
+
|
|
52
|
+
expect(qp.parse({
|
|
53
|
+
'even': []
|
|
54
|
+
})).to.deep.equal(_.range(0, 10, 2));
|
|
55
|
+
expect(qp.parse({
|
|
56
|
+
'odd': []
|
|
57
|
+
})).to.deep.equal(_.range(1, 10, 2));
|
|
58
|
+
});
|
|
59
|
+
it('should correctly parse a query with arguments', function() {
|
|
60
|
+
var qs = new QuerySandbox(10);
|
|
61
|
+
var qp = new QueryParser({
|
|
62
|
+
'greater': qs.greater
|
|
63
|
+
},
|
|
64
|
+
qs);
|
|
65
|
+
|
|
66
|
+
expect(qp.parse({
|
|
67
|
+
'greater': [2]
|
|
68
|
+
})).to.deep.equal(_.range(3, 10));
|
|
69
|
+
});
|
|
70
|
+
it('should correctly use the operator $and', function() {
|
|
71
|
+
var qs = new QuerySandbox(10);
|
|
72
|
+
var qp = new QueryParser({
|
|
73
|
+
'even': qs.even,
|
|
74
|
+
'greater': qs.greater
|
|
75
|
+
},
|
|
76
|
+
qs);
|
|
77
|
+
|
|
78
|
+
expect(qp.parse({
|
|
79
|
+
'$and': [{
|
|
80
|
+
'even': []
|
|
81
|
+
}, {
|
|
82
|
+
'greater': [2]
|
|
83
|
+
}]
|
|
84
|
+
})).to.deep.equal(_.range(4, 10, 2));
|
|
85
|
+
});
|
|
86
|
+
it('should correctly use the operator $or', function() {
|
|
87
|
+
var qs = new QuerySandbox(10);
|
|
88
|
+
var qp = new QueryParser({
|
|
89
|
+
'even': qs.even,
|
|
90
|
+
'odd': qs.odd
|
|
91
|
+
},
|
|
92
|
+
qs);
|
|
93
|
+
|
|
94
|
+
expect(qp.parse({
|
|
95
|
+
'$or': [{
|
|
96
|
+
'even': []
|
|
97
|
+
}, {
|
|
98
|
+
'odd': []
|
|
99
|
+
}]
|
|
100
|
+
}).sort()).to.deep.equal(_.range(0, 10));
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should correctly use the operator $xor', function() {
|
|
104
|
+
var qs = new QuerySandbox(10);
|
|
105
|
+
var qp = new QueryParser({
|
|
106
|
+
'greater': qs.greater,
|
|
107
|
+
'lesser': qs.lesser
|
|
108
|
+
},
|
|
109
|
+
qs);
|
|
110
|
+
|
|
111
|
+
expect(qp.parse({
|
|
112
|
+
'$xor': [{
|
|
113
|
+
'greater': [0]
|
|
114
|
+
}, {
|
|
115
|
+
'lesser': [9]
|
|
116
|
+
}]
|
|
117
|
+
}).sort()).to.deep.equal([0, 9]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should interpret a dictionary with multiple keys as $and queries', function() {
|
|
121
|
+
|
|
122
|
+
var qs = new QuerySandbox(10);
|
|
123
|
+
var qp = new QueryParser({
|
|
124
|
+
'greater': qs.greater,
|
|
125
|
+
'lesser': qs.lesser
|
|
126
|
+
}, qs);
|
|
127
|
+
|
|
128
|
+
expect(qp.parse({
|
|
129
|
+
'greater': 2,
|
|
130
|
+
'lesser': 6
|
|
131
|
+
}).sort()).to.deep.equal([3, 4, 5]);
|
|
132
|
+
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
});
|
package/test/tensor.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import chai from 'chai'
|
|
5
|
+
import chaiAlmost from 'chai-almost'
|
|
6
|
+
|
|
7
|
+
import * as mjs from 'mathjs'
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
TensorData
|
|
11
|
+
} from '../lib/tensor.js'
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
getIsotopeData
|
|
15
|
+
} from '../lib/data.js'
|
|
16
|
+
|
|
17
|
+
chai.use(chaiAlmost(1e-3));
|
|
18
|
+
|
|
19
|
+
const expect = chai.expect;
|
|
20
|
+
|
|
21
|
+
describe('#tensordata', function() {
|
|
22
|
+
|
|
23
|
+
it('should properly separate the symmetric part of a tensor', function() {
|
|
24
|
+
var td = new TensorData([
|
|
25
|
+
[0, 2, 1],
|
|
26
|
+
[0, 0, 1],
|
|
27
|
+
[1, 1, 0]
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
expect(td.symmetric).to.deep.equal([
|
|
31
|
+
[0, 1, 1],
|
|
32
|
+
[1, 0, 1],
|
|
33
|
+
[1, 1, 0]
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
expect(td.asymmetric).to.deep.equal([
|
|
37
|
+
[0, 1, 0],
|
|
38
|
+
[-1, 0, 0],
|
|
39
|
+
[0, 0, 0]
|
|
40
|
+
]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should compute and order eigenvalues properly', function() {
|
|
44
|
+
|
|
45
|
+
var td = new TensorData([
|
|
46
|
+
[1, 2, 3],
|
|
47
|
+
[2, 3, 4],
|
|
48
|
+
[3, 4, 5]
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
expect(td.eigenvalues).to.deep.almost.equal([-6.234754e-01, 0,
|
|
52
|
+
9.623475e+00
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
// Reconstruct the matrix
|
|
56
|
+
var ev = td.eigenvectors;
|
|
57
|
+
var D = mjs.diag(td.eigenvalues);
|
|
58
|
+
var evT = mjs.transpose(ev);
|
|
59
|
+
expect(mjs.multiply(ev, mjs.multiply(D, evT))).to.deep.almost.equal(td.symmetric);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should change bases properly', function() {
|
|
63
|
+
|
|
64
|
+
var td0 = new TensorData([
|
|
65
|
+
[15, 2, 2],
|
|
66
|
+
[ 2, 3, 6],
|
|
67
|
+
[ 2, 6, 9]
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
var td1 = td0.rotate(td0.eigenvectors);
|
|
71
|
+
expect(td1.symmetric).to.deep.almost.equal(mjs.diag(td0.eigenvalues));
|
|
72
|
+
|
|
73
|
+
// And vice versa...
|
|
74
|
+
var td2 = td1.rotate(td0.eigenvectors, true);
|
|
75
|
+
expect(td2.symmetric).to.deep.almost.equal(td0.symmetric);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should order eigenvalues properly following the Haeberlen convention', function() {
|
|
79
|
+
|
|
80
|
+
var td = new TensorData([
|
|
81
|
+
[1,0,0],
|
|
82
|
+
[0,2,0],
|
|
83
|
+
[0,0,-6]
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
// Haeberlen order:
|
|
87
|
+
// e_x = 2
|
|
88
|
+
// e_y = 1
|
|
89
|
+
// e_z = -6
|
|
90
|
+
|
|
91
|
+
expect(td.isotropy).to.equal(-1);
|
|
92
|
+
expect(td.haeberlen_eigenvalues).to.deep.equal([2, 1, -6]);
|
|
93
|
+
expect(td.anisotropy).to.equal(-7.5);
|
|
94
|
+
expect(td.reduced_anisotropy).to.equal(-5);
|
|
95
|
+
expect(td.asymmetry).to.equal(0.2);
|
|
96
|
+
expect(td.span).to.equal(8);
|
|
97
|
+
expect(td.skew).to.almost.equal(0.75);
|
|
98
|
+
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it ('should convert properly an EFG tensor to Hz', function() {
|
|
102
|
+
|
|
103
|
+
var efg = new TensorData([
|
|
104
|
+
[ 1.05124449e-01, 1.42197546e-01, 1.53489044e+00],
|
|
105
|
+
[ 1.42197546e-01, 2.40599479e-02, -9.03880151e-01],
|
|
106
|
+
[ 1.53489044e+00, -9.03880151e-01, -1.29184397e-01]
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
// Convert to Hz
|
|
110
|
+
var Q = getIsotopeData('O', 17).Q;
|
|
111
|
+
efg = efg.efgAtomicToHz(Q);
|
|
112
|
+
|
|
113
|
+
// Comparison in kHz
|
|
114
|
+
expect(efg.haeberlen_eigenvalues[2]/1e3).to.almost.equal(11233.854188);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it ('should convert properly an ISC tensor to Hz', function() {
|
|
118
|
+
|
|
119
|
+
var isc = new TensorData([
|
|
120
|
+
[1.8373758951855776, -0.6444912603875048, 0.03379154211567881],
|
|
121
|
+
[-0.6738855911039692, 0.72064084469826, -0.4004091413405982],
|
|
122
|
+
[0.014472208799926917, -0.3990514190555465, 0.3282668712885049]
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
// Convert to Hz
|
|
126
|
+
var g1 = getIsotopeData('C', 13).gamma;
|
|
127
|
+
var g2 = getIsotopeData('H', 1).gamma;
|
|
128
|
+
isc = isc.iscAtomicToHz(g1, g2);
|
|
129
|
+
|
|
130
|
+
// Comparison in kHz
|
|
131
|
+
expect(isc.haeberlen_eigenvalues[2]).to.almost.equal(6.53565087);
|
|
132
|
+
});
|
|
133
|
+
});
|