@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.
Files changed (100) hide show
  1. package/.eslintrc.json +16 -0
  2. package/.github/workflows/test-mocha.yml +30 -0
  3. package/.vscode/settings.json +4 -0
  4. package/LICENSE +21 -0
  5. package/README.html +1127 -0
  6. package/README.md +76 -0
  7. package/demo/demo.css +30 -0
  8. package/demo/index.html +76 -0
  9. package/demo/main.js +143 -0
  10. package/docs/.nojekyll +0 -0
  11. package/docs-tutorials/Events.md +57 -0
  12. package/docs-tutorials/Queries.md +50 -0
  13. package/fonts/Rubik/OFL.txt +93 -0
  14. package/fonts/Rubik/README.txt +77 -0
  15. package/fonts/Rubik/Rubik-Italic-VariableFont_wght.ttf +0 -0
  16. package/fonts/Rubik/Rubik-VariableFont_wght.ttf +0 -0
  17. package/fonts/Rubik/static/Rubik-Black.ttf +0 -0
  18. package/fonts/Rubik/static/Rubik-BlackItalic.ttf +0 -0
  19. package/fonts/Rubik/static/Rubik-Bold.ttf +0 -0
  20. package/fonts/Rubik/static/Rubik-BoldItalic.ttf +0 -0
  21. package/fonts/Rubik/static/Rubik-ExtraBold.ttf +0 -0
  22. package/fonts/Rubik/static/Rubik-ExtraBoldItalic.ttf +0 -0
  23. package/fonts/Rubik/static/Rubik-Italic.ttf +0 -0
  24. package/fonts/Rubik/static/Rubik-Light.ttf +0 -0
  25. package/fonts/Rubik/static/Rubik-LightItalic.ttf +0 -0
  26. package/fonts/Rubik/static/Rubik-Medium.ttf +0 -0
  27. package/fonts/Rubik/static/Rubik-MediumItalic.ttf +0 -0
  28. package/fonts/Rubik/static/Rubik-Regular.ttf +0 -0
  29. package/fonts/Rubik/static/Rubik-SemiBold.ttf +0 -0
  30. package/fonts/Rubik/static/Rubik-SemiBoldItalic.ttf +0 -0
  31. package/index.html +25 -0
  32. package/index.js +11 -0
  33. package/jsconf.json +14 -0
  34. package/lib/assets/fonts/Rubik-Medium.fnt +297 -0
  35. package/lib/assets/fonts/Rubik-Medium.png +0 -0
  36. package/lib/assets/fonts/bmpfonts.in.js +16 -0
  37. package/lib/assets/fonts/bmpfonts.js +9 -0
  38. package/lib/assets/fonts/font.js +82 -0
  39. package/lib/assets/fonts/index.js +14 -0
  40. package/lib/assets/fonts/threebmfont.js +28 -0
  41. package/lib/data.js +125 -0
  42. package/lib/formats/cell.js +114 -0
  43. package/lib/formats/cif.js +22 -0
  44. package/lib/formats/magres.js +337 -0
  45. package/lib/formats/xyz.js +124 -0
  46. package/lib/loader.js +87 -0
  47. package/lib/model.js +2076 -0
  48. package/lib/modelview.js +382 -0
  49. package/lib/nmrdata.js +2898 -0
  50. package/lib/orbit.js +1233 -0
  51. package/lib/primitives/atoms.js +261 -0
  52. package/lib/primitives/cell.js +160 -0
  53. package/lib/primitives/dither.js +156 -0
  54. package/lib/primitives/ellipsoid.js +183 -0
  55. package/lib/primitives/geometries.js +20 -0
  56. package/lib/primitives/index.js +48 -0
  57. package/lib/primitives/isosurface.js +171 -0
  58. package/lib/primitives/shapes.js +100 -0
  59. package/lib/primitives/sprites.js +172 -0
  60. package/lib/query.js +158 -0
  61. package/lib/render.js +440 -0
  62. package/lib/selbox.js +361 -0
  63. package/lib/shaders/aura.frag +26 -0
  64. package/lib/shaders/aura.vert +37 -0
  65. package/lib/shaders/dither.frag +42 -0
  66. package/lib/shaders/dither.vert +8 -0
  67. package/lib/shaders/index.in.js +17 -0
  68. package/lib/shaders/index.js +25 -0
  69. package/lib/shaders/msdf300.frag +25 -0
  70. package/lib/shaders/msdf300.vert +45 -0
  71. package/lib/tensor.js +227 -0
  72. package/lib/utils.js +168 -0
  73. package/lib/visualizer.js +480 -0
  74. package/package.json +106 -0
  75. package/scripts/build-bundle.js +17 -0
  76. package/scripts/build-fonts.js +43 -0
  77. package/scripts/build-resources.js +46 -0
  78. package/scripts/plugins-shim.js +10 -0
  79. package/test/chemdata.js +69 -0
  80. package/test/data/CHA.cif +74 -0
  81. package/test/data/H2O.xyz +8 -0
  82. package/test/data/H2_bound.xyz +4 -0
  83. package/test/data/ethanol.cell +25 -0
  84. package/test/data/ethanol.magres +238 -0
  85. package/test/data/example_single.cif +789 -0
  86. package/test/data/frac.cell +8 -0
  87. package/test/data/org.cif +427 -0
  88. package/test/data/pyridine.xyz +13 -0
  89. package/test/data/si8.xyz +10 -0
  90. package/test/loader.js +107 -0
  91. package/test/model.js +368 -0
  92. package/test/query.js +135 -0
  93. package/test/tensor.js +133 -0
  94. package/test/test-html/examples.js +1485 -0
  95. package/test/test-html/index.html +33 -0
  96. package/test/test-html/index.js +279 -0
  97. package/tools/compile_colors.py +120 -0
  98. package/tools/compile_periodic.py +96 -0
  99. package/tools/ptable.json +497 -0
  100. 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
+ });