UNPKG

@ccp-nc/crystvis-js

Version:

A Three.js based crystallographic visualisation tool

129 lines (115 loc) 3.77 kB
'use strict'; /** * @fileoverview Function for loading XYZ files * @module */ import _ from 'lodash'; import { Atoms } from '@ccp-nc/crystcif-parse'; function load(contents, filename='xyz') { // Split the file into lines let lines = _.split(contents, '\n'); // Parse the first line: number of atoms let N = parseInt(_.trim(lines[0])); if (isNaN(N) || lines.length < N + 2) { throw Error('Invalid XYZ file'); } // Parse the second line: comments let info = lines[1]; // Check if it's extended format let ext = false; let rext = /([A-Za-z]+)=(([A-Za-z0-9.:]+)|"([\s0-9.e-]+)")/g; let m = rext.exec(info); let matches = []; let cell = null; let arrays = []; while (m != null) { matches.push(m); m = rext.exec(info); } if (matches.length > 0) { // It's extended! But does it have the right keywords? let props = {}; for (let i = 0; i < matches.length; ++i) { m = matches[i]; props[m[1]] = m[3] || m[4]; } if ((_.has(props, 'Lattice') && _.has(props, 'Properties'))) { // It's valid! ext = true; // Parse the lattice let latt = _.split(props['Lattice'], /\s+/); cell = []; for (let i = 0; i < 3; ++i) { cell.push(_.map(latt.slice(3 * i, 3 * i + 3), parseFloat)); if (cell[i].indexOf(NaN) > -1) { throw Error('Invalid Extended XYZ file'); } } if (props['Properties'].slice(0, 19) != 'species:S:1:pos:R:3') { throw Error('Invalid Extended XYZ file'); } // Parse the properties let propre = /([A-Za-z]+):(S|R|I):([0-9]+)/g; let columns = []; m = propre.exec(props['Properties']) while (m != null) { columns.push({ 'name': m[1], 'type': m[2], 'n': parseInt(m[3]), 'val': [], }); m = propre.exec(props['Properties']); } // We know the first two are just species and pos arrays = columns.slice(2); } info = _.omit(props, ['Lattice', 'Properties']); } else { info = { 'comment': info }; } // Parse the following lines: atoms let elems = []; let pos = []; for (let i = 0; i < N; ++i) { let lspl = _.split(lines[i + 2], /\s+/); elems.push(lspl[0]); pos.push(_.map(lspl.slice(1, 4), parseFloat)); if (pos.indexOf(NaN) > -1) { throw Error('Invalid XYZ file'); } // Any additional parsing required? if (ext) { lspl.splice(0, 4); for (let j = 0; j < arrays.length; ++j) { let v = []; let parser = { 'S': String, 'R': parseFloat, 'I': parseInt, }[arrays[j].type]; for (let k = 0; k < arrays[j].n; ++k) { v.push(parser(lspl.splice(0))); } v = v.length > 1 ? v : v[0]; arrays[j].val.push(v); } } } // check if we have a cell if (cell === null) { throw Error('No cell found in XYZ file. Please use the Extended XYZ format.'); } let a = new Atoms(elems, pos, cell, info); if (ext) { for (let i = 0; i < arrays.length; ++i) { a.set_array(arrays[i].name, arrays[i].val); } } let structs = {}; structs[filename] = a; return structs; } export { load };