@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/lib/data.js ADDED
@@ -0,0 +1,125 @@
1
+ 'use strict';
2
+
3
+ import {
4
+ PeriodicTable as PeriodicTable
5
+ } from 'mendeleev';
6
+
7
+ import nmrData from './nmrdata.js';
8
+ import { shiftCpkColor } from './utils.js';
9
+
10
+ /**
11
+ * @fileoverview Various data arrays and structures storing useful information
12
+ * @private
13
+ */
14
+
15
+ // Van der Waals radii by atomic number
16
+ const vdwRadii = [1.77, // Z = 0 is used as the default
17
+ 1.2, 1.4, 1.82, 1.7, 2.08,
18
+ 1.95, 1.55, 1.7, 1.73, 1.54, 2.27, 1.73, 2.05,
19
+ 2.1, 2.08, 2.0, 1.97, 1.88, 2.75, 1.973, 1.7,
20
+ 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.63, 1.4, 1.39,
21
+ 1.87, 1.7, 1.85, 1.9, 2.1, 2.02, 1.7, 1.7, 1.7,
22
+ 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.63, 1.72, 1.58,
23
+ 1.93, 2.17, 2.2, 2.06, 2.15, 2.16, 1.7, 1.7,
24
+ 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7,
25
+ 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7,
26
+ 1.7, 1.7, 1.7, 1.72, 1.66, 1.55, 1.96, 2.02,
27
+ 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7,
28
+ 1.86, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7, 1.7,
29
+ 1.7, 1.7, 1.7
30
+ ];
31
+
32
+ const cpkColors = [0xff1493, // Z = 0 is used as the default
33
+ 0xffffff, 0xd9ffff, 0xcc80ff, 0xc2ff00, 0xffb5b5, 0x909090, 0x3050f8, 0xff0d0d,
34
+ 0x90e050, 0xb3e3f5, 0xab5cf2, 0x8aff00, 0xbfa6a6, 0xf0c8a0, 0xff8000, 0xffff30,
35
+ 0x1ff01f, 0x80d1e3, 0x8f40d4, 0x3dff00, 0xe6e6e6, 0xbfc2c7, 0xa6a6ab, 0x8a99c7,
36
+ 0x9c7ac7, 0xe06633, 0xf090a0, 0x50d050, 0xc88033, 0x7d80b0, 0xc28f8f, 0x668f8f,
37
+ 0xbd80e3, 0xffa100, 0xa62929, 0x5cb8d1, 0x702eb0, 0x00ff00, 0x94ffff, 0x94e0e0,
38
+ 0x73c2c9, 0x54b5b5, 0x3b9e9e, 0x248f8f, 0x0a7d8c, 0x006985, 0xc0c0c0, 0xffd98f,
39
+ 0xa67573, 0x668080, 0x9e63b5, 0xd47a00, 0x940094, 0x429eb0, 0x57178f, 0x00c900,
40
+ 0x70d4ff, 0xffffc7, 0xd9ffc7, 0xc7ffc7, 0xa3ffc7, 0x8fffc7, 0x61ffc7, 0x45ffc7,
41
+ 0x30ffc7, 0x1fffc7, 0x00ff9c, 0x00e675, 0x00d452, 0x00bf38, 0x00ab24, 0x4dc2ff,
42
+ 0x4da6ff, 0x2194d6, 0x267dab, 0x266696, 0x175487, 0xd0d0e0, 0xffd123, 0xb8b8d0,
43
+ 0xa6544d, 0x575961, 0x9e4fb5, 0xab5c00, 0x754f45, 0x428296, 0x420066, 0x007d00,
44
+ 0x70abfa, 0x00baff, 0x00a1ff, 0x008fff, 0x0080ff, 0x006bff, 0x545cf2, 0x785ce3,
45
+ 0x8a4fe3, 0xa136d4, 0xb31fd4, 0xb31fba, 0xb30da6, 0xbd0d87, 0xc70066, 0xcc0059,
46
+ 0xd1004f, 0xd90045, 0xe00038, 0xe6002e, 0xeb0026
47
+ ];
48
+
49
+
50
+ function getVdwRadius(symbol) {
51
+ var el = PeriodicTable.getElement(symbol);
52
+ var Z = (el ? el.number : 0);
53
+ return vdwRadii[Z];
54
+ }
55
+
56
+ function getCpkColor(symbol, isotope=null) {
57
+ var el = PeriodicTable.getElement(symbol);
58
+ var Z = (el ? el.number : 0);
59
+ var cpkc = cpkColors[Z];
60
+
61
+ if (isotope !== null) {
62
+ // We apply an isotope correction
63
+ var edata = nmrData[symbol];
64
+ if (isotope != edata.maxiso) {
65
+ // Then a correction is necessary
66
+ var isolist = getIsotopeList(symbol);
67
+ var imax = isolist.indexOf(edata.maxiso);
68
+ var ic = isolist.indexOf(isotope.toString());
69
+ var shift = (ic-imax)/isolist.length;
70
+ cpkc = shiftCpkColor(cpkc, shift);
71
+ }
72
+ }
73
+
74
+ return cpkc;
75
+ }
76
+
77
+ function getIsotopeList(symbol) {
78
+ const data = nmrData[symbol];
79
+ if (!data) {
80
+ return null;
81
+ }
82
+
83
+ // If we do have it though...
84
+ return Object.keys(data.isotopes).sort();
85
+ }
86
+
87
+ function getElementData(symbol) {
88
+ return nmrData[symbol];
89
+ }
90
+
91
+ function getIsotopeData(symbol, iso=null) {
92
+ const eldata = nmrData[symbol];
93
+
94
+ if (eldata == null) {
95
+ return null;
96
+ }
97
+
98
+ if (iso === null) {
99
+ // Make it the most common one
100
+ iso = eldata.maxiso;
101
+ }
102
+ else if (iso === 'nmr') {
103
+ iso = eldata.maxiso_NMR;
104
+ }
105
+ else if (iso === 'Q') {
106
+ iso = eldata.maxiso_Q;
107
+ }
108
+
109
+ if (iso === null) {
110
+ // No such isotope
111
+ return null;
112
+ }
113
+
114
+ return eldata.isotopes[iso];
115
+ }
116
+
117
+ export {
118
+ vdwRadii,
119
+ cpkColors,
120
+ getVdwRadius,
121
+ getCpkColor,
122
+ getIsotopeList,
123
+ getElementData,
124
+ getIsotopeData
125
+ }
@@ -0,0 +1,114 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Function for loading CASTEP's Cell files
5
+ * @module
6
+ */
7
+
8
+ import _ from 'lodash';
9
+ import {
10
+ Atoms
11
+ } from 'crystcif-parse';
12
+
13
+ function cellBlocks(lines, units={}) {
14
+ const block_re = /%BLOCK\s+([A-Z_]+)/;
15
+ const endblock_re = /%ENDBLOCK\s+([A-Z_]+)/;
16
+
17
+ let blocks = {};
18
+ _.forEach(lines, (l, i) => {
19
+
20
+ let lu = l.toUpperCase();
21
+ let ms = block_re.exec(lu);
22
+ let me = endblock_re.exec(lu);
23
+
24
+ if (ms) {
25
+ let name = ms[1].toUpperCase();
26
+ if (name in blocks) {
27
+ throw Error('Duplicated ' + name + ' block found');
28
+ }
29
+ blocks[name] = {start: i};
30
+ } else if (me) {
31
+ let name = me[1].toUpperCase();
32
+ if (!(name in blocks)) {
33
+ throw Error('Block ' + name + ' ends without starting');
34
+ }
35
+
36
+ blocks[name]['end'] = i;
37
+ // Assign the contents
38
+ let i0 = blocks[name].start;
39
+ // Identify any units
40
+ let ul = lines[i0+1].trim().toLowerCase();
41
+ if (units[name] && (ul === units[name] || units[name].includes(ul))) {
42
+ i0 += 1;
43
+ blocks[name]['units'] = ul;
44
+ }
45
+ blocks[name]['lines'] = lines.slice(i0+1, i);
46
+ }
47
+ });
48
+
49
+ return blocks;
50
+ }
51
+
52
+ function load(contents, filename='cell') {
53
+
54
+ // Split the file into lines
55
+ let lines = _.split(contents, '\n');
56
+
57
+ // Admissible units
58
+ const units = {
59
+ 'LATTICE_CART': ['ang'],
60
+ 'POSITIONS_ABS': ['ang']
61
+ };
62
+
63
+ // Find blocks
64
+ let blocks = cellBlocks(lines, units);
65
+
66
+ let pabs = ('POSITIONS_ABS' in blocks);
67
+ let pfrac = ('POSITIONS_FRAC' in blocks);
68
+ let ccart = ('LATTICE_CART' in blocks);
69
+ let cabc = ('LATTICE_ABC' in blocks);
70
+
71
+ switch(pabs+pfrac) {
72
+ case 0:
73
+ throw Error('No positions block found');
74
+ case 2:
75
+ throw Error('Duplicated positions blocks found');
76
+ default:
77
+ break;
78
+ }
79
+
80
+ switch(ccart+cabc) {
81
+ case 0:
82
+ throw Error('No lattice block found');
83
+ case 2:
84
+ throw Error('Duplicated lattice blocks found');
85
+ default:
86
+ break;
87
+ }
88
+
89
+ // Parse the cell
90
+ let cell = ccart? blocks['LATTICE_CART'].lines : blocks['LATTICE_ABC'].lines;
91
+ cell = cell.map((l) => (_.trim(l).split(/\s+/).map(parseFloat)));
92
+
93
+ let elems = [];
94
+ let positions = [];
95
+
96
+ let pblock = pabs? blocks['POSITIONS_ABS'].lines : blocks['POSITIONS_FRAC'].lines;
97
+
98
+ pblock.forEach((l) => {
99
+ l = _.trim(l).split(/\s+/);
100
+ if (l.length < 4)
101
+ throw Error('Incomplete line in positions block');
102
+ elems.push(l[0]);
103
+ positions.push(l.slice(1,4).map(parseFloat));
104
+ });
105
+
106
+ var a = new Atoms(elems, positions, cell, {}, pfrac);
107
+
108
+ var structs = {};
109
+ structs[filename] = a;
110
+
111
+ return structs;
112
+ }
113
+
114
+ export { load };
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Function for loading CIF files (wrapper)
5
+ * @module
6
+ */
7
+
8
+ import _ from 'lodash';
9
+ import { Atoms } from 'crystcif-parse';
10
+
11
+ function load(contents, filename) {
12
+
13
+ let structs = Atoms.readCif(contents);
14
+
15
+ if (filename) {
16
+ structs = _.mapKeys(structs, (v, k) => (filename + '_' + k));
17
+ }
18
+
19
+ return structs;
20
+ }
21
+
22
+ export { load };
@@ -0,0 +1,337 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Function for loading Magres files
5
+ * @module
6
+ */
7
+
8
+ import _ from 'lodash';
9
+ import {
10
+ Atoms
11
+ } from 'crystcif-parse';
12
+ import {
13
+ TensorData
14
+ } from '../tensor.js';
15
+
16
+ const MagresUnits = {
17
+ length: {
18
+ ang: 1.0,
19
+ Angstrom: 1.0
20
+ },
21
+ sus: {
22
+ '10^-6.cm^3.mol^-1': 1.0
23
+ },
24
+ ms: {
25
+ ppm: 1.0
26
+ },
27
+ efg: {
28
+ au: 1.0
29
+ },
30
+ isc: {
31
+ '10^19.T^2.J^-1': 1.0
32
+ }
33
+ };
34
+
35
+ // Magres parsing utility functions
36
+ function parseNoAtomLine(line, units) {
37
+ // Assumed to be just nine numbers, a full 3x3 matrix.
38
+ // Used for susceptibility
39
+ let tens = [];
40
+ for (let i = 0; i < 3; ++i) {
41
+ tens.push([]);
42
+ for (let j = 0; j < 3; ++j) {
43
+ tens[i].push(parseFloat(line[3 * i + j]) * units);
44
+ }
45
+ }
46
+
47
+ return new TensorData(tens);
48
+ }
49
+
50
+ function parseOneAtomLine(line, units, labels) {
51
+ // Species label + index, then nine numbers
52
+ let sp = line[0];
53
+ let sp_i = parseInt(line[1]);
54
+ let i = _.findIndex(labels, function(x) {
55
+ return (x[0] == sp) && (x[1] == sp_i);
56
+ });
57
+ let ltrim = Array.from(line).splice(2);
58
+
59
+ let ans = {
60
+ i: i,
61
+ tens: parseNoAtomLine(ltrim, units)
62
+ }
63
+
64
+ return ans;
65
+ }
66
+
67
+ function parseTwoAtomLine(line, units, labels) {
68
+ let sp1 = line[0]
69
+ let sp1_i = parseInt(line[1]);
70
+ let i1 = _.findIndex(labels, function(x) {
71
+ return (x[0] == sp1) && (x[1] == sp1_i);
72
+ });
73
+ let ltrim = Array.from(line).splice(2);
74
+
75
+ let ans = parseOneAtomLine(ltrim, units, labels);
76
+
77
+ ans = {
78
+ i1: i1,
79
+ i2: ans.i,
80
+ tens: ans.tens
81
+ };
82
+
83
+ return ans;
84
+ }
85
+
86
+ const MagresParsers = {
87
+ sus: parseNoAtomLine,
88
+ ms: parseOneAtomLine,
89
+ efg: parseOneAtomLine,
90
+ isc: parseTwoAtomLine
91
+ };
92
+
93
+ function load(contents, filename='magres') {
94
+
95
+ const known_blocks = ['atoms', 'magres'];
96
+
97
+ let lines = _.split(contents, '\n');
98
+
99
+ // First line contains version
100
+ let v_re = /#\$magres-abinitio-v([0-9]+\.[0-9]+)/;
101
+ let v_match = lines[0].match(v_re);
102
+
103
+ if (!v_match) {
104
+ throw Error('Invalid Magres file format: no version line');
105
+ }
106
+ let version = v_match[1];
107
+
108
+ // Start by identifying blocks
109
+ let blocks = {};
110
+ let b_re = /\[(\/)?([a-zA-Z_]+)\]/;
111
+ let b_alt_re = /<(\/)?([a-zA-Z_]+)>/; // Alternative version (old magres tags)
112
+ let mtagtype_detected = false; // Have we found out which type of tags are used?
113
+ let block_name = null;
114
+ let block_lines = [];
115
+
116
+ for (let i = 1; i < lines.length; ++i) {
117
+
118
+ let b_match = lines[i].match(b_re);
119
+
120
+ if (!mtagtype_detected) {
121
+ let b_alt_match = lines[i].match(b_alt_re);
122
+ if (b_alt_match) {
123
+ mtagtype_detected = true;
124
+ b_re = b_alt_re;
125
+ b_match = b_alt_match;
126
+ }
127
+ else if (b_match) {
128
+ mtagtype_detected = true;
129
+ }
130
+ }
131
+
132
+ if (b_match) {
133
+ // Start or end?
134
+ if (b_match[1] == '/') {
135
+ if (b_match[2] == block_name) {
136
+ // End block
137
+ blocks[block_name] = block_lines;
138
+ block_name = null;
139
+ block_lines = [];
140
+ } else {
141
+ throw Error('Invalid Magres file format: block closed without opening');
142
+ }
143
+ } else {
144
+ if (block_name) {
145
+ throw Error('Invalid Magres file format: block opened without closing');
146
+ }
147
+ block_name = b_match[2];
148
+ }
149
+ } else if (block_name) {
150
+ block_lines.push(lines[i]);
151
+ }
152
+ }
153
+
154
+ // Process each block
155
+ for (let bname in blocks) {
156
+ let block = blocks[bname];
157
+
158
+ if (!known_blocks.includes(bname)) {
159
+ // We don't know this block
160
+ blocks[bname] = _.join(block, '\n');
161
+ continue;
162
+ }
163
+
164
+ let data = {};
165
+
166
+ for (let i = 0; i < block.length; ++i) {
167
+ let l = block[i];
168
+ let lspl = _.trim(l).split(/\s+/);
169
+ // Is it a 'units' line?
170
+ if (lspl[0] === 'units') {
171
+ let tag = lspl[1];
172
+ if (tag in data && data[tag].lines.length > 0) {
173
+ // We're being forgiving only of the case where a unit definition is duplicated
174
+ throw Error('Invalid Magres file format: units specified after tag ' + tag + ' has been used');
175
+ }
176
+ data[tag] = {
177
+ 'units': lspl[2],
178
+ 'lines': []
179
+ };
180
+ } else if (lspl[0] !== '') {
181
+ let tag = lspl[0];
182
+ if (!(tag in data)) {
183
+ data[tag] = {
184
+ 'units': null,
185
+ 'lines': []
186
+ };
187
+ }
188
+ data[tag].lines.push(lspl.splice(1));
189
+ }
190
+ }
191
+
192
+ blocks[bname] = data;
193
+
194
+ }
195
+
196
+ // Now on to read the blocks themselves
197
+
198
+ let ablock = blocks.atoms;
199
+
200
+ if (!ablock) {
201
+ throw Error('Invalid Magres file format: does not contain atoms block');
202
+ }
203
+
204
+ // Read in the cell, if present
205
+ let cell = null;
206
+ if ('lattice' in ablock) {
207
+ let u = 1.0;
208
+ let uname = ablock.lattice.units;
209
+ if (uname) {
210
+ u = MagresUnits.length[uname];
211
+ if (!u) {
212
+ throw Error('Invalid Magres file format: invalid units for cell');
213
+ }
214
+ }
215
+ cell = [];
216
+ let line = Array.from(ablock.lattice.lines[0]);
217
+ for (let i = 0; i < 3; ++i) {
218
+ cell.push(_.map(line.splice(0, 3), function(x) {
219
+ return parseFloat(x) * u;
220
+ }));
221
+ }
222
+ }
223
+
224
+ // Read in the atom positions and species
225
+ let elems = [];
226
+ let pos = [];
227
+ let mlabels = [];
228
+ let labels = [];
229
+
230
+ if ('atom' in ablock) {
231
+ let u = 1.0;
232
+ let uname = ablock.atom.units;
233
+ if (uname) {
234
+ u = MagresUnits.length[uname];
235
+ if (!u) {
236
+ throw Error('Invalid Magres file format: invalid units for atom');
237
+ }
238
+ }
239
+
240
+ for (let i = 0; i < ablock.atom.lines.length; ++i) {
241
+ let l = ablock.atom.lines[i];
242
+
243
+ elems.push(l[0]);
244
+ mlabels.push([l[1], parseInt(l[2])]);
245
+ labels.push(l[1]);
246
+
247
+ pos.push(_.map(l.splice(3), function(x) {
248
+ return parseFloat(x) * u;
249
+ }));
250
+ }
251
+
252
+ } else {
253
+ throw Error('Invalid Magres file format: no atom position data found');
254
+ }
255
+
256
+ // Create atoms object
257
+ let atoms = new Atoms(elems, pos, cell, {
258
+ 'magres-blocks': blocks,
259
+ 'magres-version': version,
260
+ });
261
+ // Add array
262
+ atoms.set_array('labels', labels);
263
+ atoms.set_array('magres-labels', mlabels);
264
+
265
+ let N = elems.length;
266
+
267
+ // Now for the magres stuff
268
+ let mblock = blocks.magres;
269
+
270
+ if (mblock) {
271
+
272
+ for (let tag in mblock) {
273
+ let tag_type = tag.split('_')[0];
274
+
275
+ let u = 1.0;
276
+ let uname = mblock[tag].units;
277
+ if (uname) {
278
+ u = MagresUnits[tag_type][uname];
279
+ if (!u) {
280
+ throw Error('Invalid Magres file format: invalid units for ' + tag);
281
+ }
282
+ }
283
+
284
+ // Initialise as dictionary
285
+ let tag_dimension;
286
+ let tag_data;
287
+ switch (MagresParsers[tag_type]) {
288
+ case parseNoAtomLine:
289
+ tag_dimension = 0;
290
+ break;
291
+ case parseOneAtomLine:
292
+ tag_dimension = 1;
293
+ tag_data = new Array(N);
294
+ break;
295
+ case parseTwoAtomLine:
296
+ tag_dimension = 2;
297
+ tag_data = _.times(N, function() {
298
+ return new Array(N);
299
+ });
300
+ }
301
+
302
+ for (let i = 0; i < mblock[tag].lines.length; ++i) {
303
+ let l = mblock[tag].lines[i];
304
+ let data = MagresParsers[tag_type](l, u, mlabels);
305
+ switch (tag_dimension) {
306
+ case 0:
307
+ tag_data = data;
308
+ break;
309
+ case 1:
310
+ tag_data[data.i] = data.tens;
311
+ break;
312
+ case 2:
313
+ tag_data[data.i1][data.i2] = data.tens;
314
+ tag_data[data.i2][data.i1] = data.tens;
315
+ break;
316
+ }
317
+ }
318
+
319
+ if (tag_dimension == 0) {
320
+ atoms.info[tag] = tag_data;
321
+ } else {
322
+ atoms.set_array(tag, tag_data);
323
+ }
324
+
325
+ }
326
+
327
+ }
328
+
329
+ let structs = {};
330
+ structs[filename] = atoms;
331
+
332
+ return structs;
333
+ }
334
+
335
+ export {
336
+ load
337
+ };