import { ParserOptionsSpec } from "./ParserOptionsSpec";
import { assignPDBBonds } from "./utils/assignPDBBonds";
import { computeSecondaryStructure } from "./utils/computeSecondaryStructure";
/**
* Parse a pqr file from str and create atoms. A pqr file is assumed to be a whitespace delimited PDB with charge and radius fields.
*
* @param {string}
* str
* @param {ParserOptionsSpec}
* options - noSecondaryStructure (do not compute ss)
* @category Parsers
*/
export function PQR(str: string, options: ParserOptionsSpec) {
var atoms: any[][] & Record<string, any> = [[]];
var computeStruct = !options.noSecondaryStructure;
atoms.modelData = [{symmetries:[]}];
var serialToIndex: number[] = []; // map from pdb serial to index in atoms
var lines = str.split(/\r?\n|\r/);
var line: string | string[];
for (let i = 0; i < lines.length; i++) {
line = lines[i].replace(/^\s*/, ''); // remove indent
var recordName = line.substring(0, 6);
if (recordName.indexOf("END") == 0) {
if (options.multimodel) {
if (!options.onemol)
atoms.push([]);
continue;
}
else {
break;
}
}
else if (recordName == 'ATOM ' || recordName == 'HETATM') {
// I would have liked to split based solely on whitespace, but
// it seems that there is no guarantee that all the fields will
// be filled out (e.g. the chain) so this doesn't work
var hetflag: boolean;
let serial = parseInt(line.substring(6, 11));
let atom = line.substring(12, 16).replace(/ /g, "");
let resn = line.substring(17, 20).trim();
let chain = line.substring(21, 22);
let resi = parseInt(line.substring(22, 26));
// however let's split the coordinates, charge and radius by
// whitespace
// to support extra precision
var vals = line.substring(30).trim().split(/\s+/);
var x = parseFloat(vals[0]);
var y = parseFloat(vals[1]);
var z = parseFloat(vals[2]);
var charge = parseFloat(vals[3]);
var radius = parseFloat(vals[4]);
var elem = atom[0];
if (atom.length > 1 && atom[1].toUpperCase() != atom[1]) {
// slight hack - identify two character elements by the
// second character in the atom name being lowercase
elem = atom.substring(0, 2);
}
if (line[0] == 'H')
hetflag = true;
else
hetflag = false;
serialToIndex[serial] = atoms[atoms.length-1].length;
atoms[atoms.length-1].push({
'resn' : resn,
'x' : x,
'y' : y,
'z' : z,
'elem' : elem,
'hetflag' : hetflag,
'chain' : chain,
'resi' : resi,
'serial' : serial,
'atom' : atom,
'bonds' : [],
'ss' : 'c',
'bondOrder' : [],
'properties' : {
'charge' : charge,
'partialCharge' : charge,
'radius' : radius
},
'pdbline' : line
});
} else if (recordName == 'CONECT') {
// MEMO: We don't have to parse SSBOND, LINK because both are
// also
// described in CONECT. But what about 2JYT???
var from = parseInt(line.substring(6, 11));
var fromAtom = atoms[atoms.length-1][serialToIndex[from]];
for (let j = 0; j < 4; j++) {
var to = parseInt(line.substring([ 11, 16, 21, 26 ][j], [ 11, 16, 21, 26 ][j] + 5));
var toAtom = atoms[atoms.length-1][serialToIndex[to]];
if (fromAtom !== undefined && toAtom !== undefined) {
fromAtom.bonds.push(serialToIndex[to]);
fromAtom.bondOrder.push(1);
}
}
}
}
// assign bonds - yuck, can't count on connect records
for (let i = 0; i < atoms.length; i++) {
assignPDBBonds(atoms[i],options);
if (computeStruct)
computeSecondaryStructure(atoms[i],options.hbondCutoff);
}
return atoms;
};