Skip to content

Commit

Permalink
parse insertion codes from PDB files
Browse files Browse the repository at this point in the history
PDB loading has been adapted to honor insertion codes for residues.
It also uses insertion codes for HELIX/SHEET records. There are
still a few other places that do not know about insertion codes.
Prominent examples are the coloring methods or selection by residue
number. Nonetheless, this fixes the worst of #53.
  • Loading branch information
biasmv committed Oct 31, 2014
1 parent 0820826 commit 9ec0a62
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 38 deletions.
34 changes: 17 additions & 17 deletions pdbs/1crn.pdb
Original file line number Diff line number Diff line change
Expand Up @@ -332,23 +332,23 @@ ATOM 56 CA ALA A 9 5.598 5.767 11.082 1.00 3.56 C
ATOM 57 C ALA A 9 6.441 5.527 9.850 1.00 4.13 C
ATOM 58 O ALA A 9 6.052 5.933 8.744 1.00 4.36 O
ATOM 59 CB ALA A 9 6.022 6.977 11.891 1.00 4.80 C
ATOM 60 N ARG A 10 7.647 4.909 10.005 1.00 3.73 N
ATOM 61 CA ARG A 10 8.496 4.609 8.837 1.00 3.38 C
ATOM 62 C ARG A 10 7.798 3.609 7.876 1.00 3.47 C
ATOM 63 O ARG A 10 7.878 3.778 6.651 1.00 4.67 O
ATOM 64 CB ARG A 10 9.847 4.020 9.305 1.00 3.95 C
ATOM 65 CG ARG A 10 10.752 3.607 8.149 1.00 4.55 C
ATOM 66 CD ARG A 10 11.226 4.699 7.244 1.00 5.89 C
ATOM 67 NE ARG A 10 12.143 5.571 8.035 1.00 6.20 N
ATOM 68 CZ ARG A 10 12.758 6.609 7.443 1.00 7.52 C
ATOM 69 NH1 ARG A 10 12.539 6.932 6.158 1.00 10.68 N
ATOM 70 NH2 ARG A 10 13.601 7.322 8.202 1.00 9.48 N
ATOM 71 N SER A 11 7.186 2.582 8.445 1.00 5.19 N
ATOM 72 CA SER A 11 6.500 1.584 7.565 1.00 4.60 C
ATOM 73 C SER A 11 5.382 2.313 6.773 1.00 4.84 C
ATOM 74 O SER A 11 5.213 2.016 5.557 1.00 5.84 O
ATOM 75 CB SER A 11 5.908 0.462 8.400 1.00 5.91 C
ATOM 76 OG SER A 11 6.990 -0.272 9.012 1.00 8.38 O
ATOM 60 N ARG A 10A 7.647 4.909 10.005 1.00 3.73 N
ATOM 61 CA ARG A 10A 8.496 4.609 8.837 1.00 3.38 C
ATOM 62 C ARG A 10A 7.798 3.609 7.876 1.00 3.47 C
ATOM 63 O ARG A 10A 7.878 3.778 6.651 1.00 4.67 O
ATOM 64 CB ARG A 10A 9.847 4.020 9.305 1.00 3.95 C
ATOM 65 CG ARG A 10A 10.752 3.607 8.149 1.00 4.55 C
ATOM 66 CD ARG A 10A 11.226 4.699 7.244 1.00 5.89 C
ATOM 67 NE ARG A 10A 12.143 5.571 8.035 1.00 6.20 N
ATOM 68 CZ ARG A 10A 12.758 6.609 7.443 1.00 7.52 C
ATOM 69 NH1 ARG A 10A 12.539 6.932 6.158 1.00 10.68 N
ATOM 70 NH2 ARG A 10A 13.601 7.322 8.202 1.00 9.48 N
ATOM 71 N SER A 10B 7.186 2.582 8.445 1.00 5.19 N
ATOM 72 CA SER A 10B 6.500 1.584 7.565 1.00 4.60 C
ATOM 73 C SER A 10B 5.382 2.313 6.773 1.00 4.84 C
ATOM 74 O SER A 10B 5.213 2.016 5.557 1.00 5.84 O
ATOM 75 CB SER A 10B 5.908 0.462 8.400 1.00 5.91 C
ATOM 76 OG SER A 10B 6.990 -0.272 9.012 1.00 8.38 O
ATOM 77 N ASN A 12 4.648 3.182 7.446 1.00 3.54 N
ATOM 78 CA ASN A 12 3.545 3.935 6.751 1.00 4.57 C
ATOM 79 C ASN A 12 4.107 4.851 5.691 1.00 4.14 C
Expand Down
31 changes: 19 additions & 12 deletions src/io.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,27 @@ function PDBReader() {
}

function parseHelixRecord(line) {
// FIXME: handle insertion codes
var frst_num = parseInt(line.substr(21, 4), 10);
var last_num = parseInt(line.substr(33, 4), 10);
var frstNum = parseInt(line.substr(21, 4), 10);
var frstInsCode = line[25] === ' ' ? '\0' : line[25];
var lastNum = parseInt(line.substr(33, 4), 10);
var lastInsCode = line[37] === ' ' ? '\0' : line[37];
var chainName = line[19];
return { first : frst_num, last : last_num, chainName : chainName };
return { first : [frstNum, frstInsCode],
last : [lastNum, lastInsCode], chainName : chainName
};
}

function parseSheetRecord(line) {
// FIXME: handle insertion codes
var frst_num = parseInt(line.substr(22, 4), 10);
var last_num = parseInt(line.substr(33, 4), 10);
var frstNum = parseInt(line.substr(22, 4), 10);
var frstInsCode = line[26] === ' ' ? '\0' : line[26];
var lastNum = parseInt(line.substr(33, 4), 10);
var lastInsCode = line[37] === ' ' ? '\0' : line[37];
var chainName = line[21];
return { first : frst_num, last : last_num, chainName : chainName };
return {
first : [frstNum, frstInsCode],
last : [lastNum, lastInsCode],
chainName : chainName
};
}

function Remark350Reader() {
Expand Down Expand Up @@ -130,14 +138,14 @@ function pdb(text) {
var resName = line.substr(17, 3).trim();
var atomName = line.substr(12, 4).trim();
var rnumNum = parseInt(line.substr(22, 4), 10);
var insCode = line[26];
var insCode = line[26] === ' ' ? '\0' : line[26];
var updateResidue = false;
var updateChain = false;
if (!currChain || currChain.name() !== chainName) {
updateChain = true;
updateResidue = true;
}
if (!currRes || currRes.num() !== rnumNum) {
if (!currRes || currRes.num() !== rnumNum || currRes.insCode() !== insCode) {
updateResidue = true;
}
if (updateChain) {
Expand All @@ -146,8 +154,7 @@ function pdb(text) {
currChain = structure.chain(chainName) || structure.addChain(chainName);
}
if (updateResidue) {
currRes = currChain.addResidue(resName, rnumNum,
currChain.residues().length);
currRes = currChain.addResidue(resName, rnumNum, insCode);
}
var pos = vec3.create();
for (var i=0;i<3;++i) {
Expand Down
44 changes: 35 additions & 9 deletions src/mol.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ function covalentRadius(ele) {
return 1.5;
}

// combines the numeric part of the residue number with the insertion
// code and returns a single number. Note that this is completely safe
// and we do not have to worry about overflows, as for PDB files the
// range of permitted residue numbers is quite limited anyway.
function rnumInsCodeHash(num, insCode) {
return num << 8 | insCode.charCodeAt(0);
}

//-----------------------------------------------------------------------------
// MolBase, ChainBase, ResidueBase, AtomBase
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -671,25 +679,34 @@ Chain.prototype.name = function() { return this._name; };

Chain.prototype.full = function() { return this; };

Chain.prototype.addResidue = function(name, num) {
var residue = new Residue(this, name, num);
this._rnumsOrdered = this._residues.length === 0 ||
(this._rnumsOrdered && this._residues[this._residues.length-1].num() <= num);
Chain.prototype.addResidue = function(name, num, insCode) {
insCode = insCode || '\0';
var residue = new Residue(this, name, num, insCode);
if (this._residues.length > 0 && this._rnumsOrdered) {
var combinedRNum = rnumInsCodeHash(num, insCode);
var last = this._residues[this._residues.length-1];
var lastCombinedRNum = rnumInsCodeHash(last.num(),last.insCode());
this._rnumsOrdered = lastCombinedRNum < combinedRNum;
}
this._residues.push(residue);
return residue;
};


Chain.prototype.residuesInRnumRange = function(start, end) {
// FIXME: this currently only works with the numeric part, insertion
// codes are not honoured.
var matching = [];
var i, e;
if (this._rnumsOrdered === true) {
// binary search our way to heaven
var startIdx = indexFirstLargerEqualThan(this._residues, numify(start), rnumComp);
var startIdx = indexFirstLargerEqualThan(this._residues, numify(start),
rnumComp);
if (startIdx === -1) {
return matching;
}
var endIdx = indexLastSmallerEqualThan(this._residues, numify(end), rnumComp);
var endIdx = indexLastSmallerEqualThan(this._residues, numify(end),
rnumComp);
if (endIdx === -1) {
return matching;
}
Expand All @@ -708,16 +725,19 @@ Chain.prototype.residuesInRnumRange = function(start, end) {
};

// assigns secondary structure to residues in range from_num to to_num.
Chain.prototype.assignSS = function(from_num, to_num, ss) {
Chain.prototype.assignSS = function(fromNumAndIns, toNumAndIns, ss) {
// FIXME: when the chain numbers are completely ordered, perform binary
// search to identify range of residues to assign secondary structure to.
var from = rnumInsCodeHash(fromNumAndIns[0], fromNumAndIns[1]);
var to = rnumInsCodeHash(toNumAndIns[0], toNumAndIns[1]);
for (var i = 1; i < this._residues.length-1; ++i) {
var res = this._residues[i];
// FIXME: we currently don't set the secondary structure of the first and
// last residue of helices and sheets. that takes care of better
// transitions between coils and helices. ideally, this should be done
// in the cartoon renderer, NOT in this function.
if (res.num() <= from_num || res.num() >= to_num) {
var combined = rnumInsCodeHash(res.num(), res.insCode());
if (combined <= from || combined >= to) {
continue;
}
res.setSS(ss);
Expand Down Expand Up @@ -778,10 +798,11 @@ Chain.prototype.backboneTraces = function() {

};

function Residue(chain, name, num) {
function Residue(chain, name, num, insCode) {
ResidueBase.prototype.constructor.call(this);
this._name = name;
this._num = num;
this._insCode = insCode;
this._atoms = [];
this._ss = 'C';
this._chain = chain;
Expand All @@ -791,6 +812,7 @@ function Residue(chain, name, num) {
derive(Residue, ResidueBase);

Residue.prototype.name = function() { return this._name; };
Residue.prototype.insCode = function() { return this._insCode; };

Residue.prototype.num = function() { return this._num; };

Expand Down Expand Up @@ -996,6 +1018,10 @@ ResidueView.prototype.addAtom = function(atom) {

ResidueView.prototype.full = function() { return this._residue; };
ResidueView.prototype.num = function() { return this._residue.num(); };

ResidueView.prototype.insCode = function() {
return this._residue.insCode();
};
ResidueView.prototype.ss = function() { return this._residue.ss(); };
ResidueView.prototype.index = function() { return this._residue.index(); };
ResidueView.prototype.chain = function() { return this._chainView; };
Expand Down

0 comments on commit 9ec0a62

Please sign in to comment.