parsers_VASP.ts


import { Matrix3 } from "../WebGL";

import { assignBonds } from "./utils/assignBonds";
import { ParserOptionsSpec } from "./ParserOptionsSpec";

/**
 * @param {string}
 *            str
 * @param {ParserOptionsSpec}
 *            options
 * @category Parsers
*/

export function VASP(str: string, options: ParserOptionsSpec = {}) {
  var atoms: any[][] & Record<string, any> = [[]];
  var lattice: Record<string, number | Float32Array> = {};
  const assignbonds =
    options.assignBonds === undefined ? true : options.assignBonds;

  var lines = str.replace(/^\s+/, "").split(/\r?\n/);

  if (lines.length < 3) {
    return atoms;
  }

  if (lines[1].match(/\d+/)) {
    lattice.length = parseFloat(lines[1]);
  } else {
    console.log(
      "Warning: second line of the vasp structure file must be a number"
    );
    return atoms;
  }

  if (lattice.length < 0) {
    console.log(
      "Warning: Vasp implementation for negative lattice lengths is not yet available"
    );
    return atoms;
  }

  lattice.xVec = new Float32Array((lines[2] as any).replace(/^\s+/, "").split(/\s+/));
  lattice.yVec = new Float32Array((lines[3] as any).replace(/^\s+/, "").split(/\s+/));
  lattice.zVec = new Float32Array((lines[4] as any).replace(/^\s+/, "").split(/\s+/));

  var matrix = new Matrix3(
    lattice.xVec[0],
    lattice.xVec[1],
    lattice.xVec[2],
    lattice.yVec[0],
    lattice.yVec[1],
    lattice.yVec[2],
    lattice.zVec[0],
    lattice.zVec[1],
    lattice.zVec[2]
  );

  matrix.multiplyScalar(lattice.length);
  atoms.modelData = [{ symmetries: [], cryst: { matrix: matrix } }];
  var atomSymbols = lines[5]
    .replace(/\s+/, "")
    .replace(/\s+$/, "")
    .split(/\s+/);
  var atomSpeciesNumber = new Int16Array(
    (lines[6] as any).replace(/^\s+/, "").split(/\s+/)
  );
  var vaspMode = lines[7].replace(/\s+/, "");

  var selective = false
  if (vaspMode.match(/S/)) {
    selective = true
    vaspMode = lines[8].replace(/\s+/, "");
  }

  if (vaspMode.match(/C/)) {
    vaspMode = "cartesian";
  } else if (vaspMode.match(/D/)) {
    vaspMode = "direct";
  } else {
    console.log(
      "Warning: Unknown vasp mode in POSCAR file: mode must be either C(artesian) or D(irect)"
    );
    return atoms;
  }

  if (atomSymbols.length != atomSpeciesNumber.length) {
    console.log("Warning: declaration of atomary species wrong:");
    console.log(atomSymbols);
    console.log(atomSpeciesNumber);
    return atoms;
  }

  if (selective) {
    lines.splice(0, 9);
  }
  else {
    lines.splice(0, 8);
  }

  var atomCounter = 0;

  for (var i = 0, len = atomSymbols.length; i < len; i++) {
    var atomSymbol = atomSymbols[i];
    for (var j = 0, atomLen = atomSpeciesNumber[i]; j < atomLen; j++) {
      var coords = new Float32Array(
        (lines[atomCounter + j] as any).replace(/^\s+/, "").split(/\s+/)
      );

      var atom: Record<string, number | []> = {};
      (atom.elem as any) = atomSymbol;
      if (vaspMode == "cartesian") {
        atom.x = lattice.length * coords[0];
        atom.y = lattice.length * coords[1];
        atom.z = lattice.length * coords[2];
      } else {
        atom.x =
          lattice.length *
          (coords[0] * lattice.xVec[0] +
            coords[1] * lattice.yVec[0] +
            coords[2] * lattice.zVec[0]);
        atom.y =
          lattice.length *
          (coords[0] * lattice.xVec[1] +
            coords[1] * lattice.yVec[1] +
            coords[2] * lattice.zVec[1]);
        atom.z =
          lattice.length *
          (coords[0] * lattice.xVec[2] +
            coords[1] * lattice.yVec[2] +
            coords[2] * lattice.zVec[2]);
      }

      atom.bonds = [];
      atom.bondOrder = [];

      atoms[0].push(atom);
    }
    atomCounter += atomSpeciesNumber[i];
  }

  if (assignbonds) {
    for (let i = 0; i < atoms.length; i++) {
      assignBonds(atoms[i], options);
    }
  }
  return atoms;
}