Skip to content

Commit

Permalink
Merge pull request #2637 from dkoes/segnames
Browse files Browse the repository at this point in the history
Support for segment names in pdb files
  • Loading branch information
ghutchis committed Dec 6, 2023
2 parents 182c80f + 754dad6 commit 283cc84
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 5 deletions.
5 changes: 5 additions & 0 deletions include/openbabel/residue.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ namespace OpenBabel {
//! MODRES records for modified residues:
//! http://www.rcsb.org/pdb/file_formats/pdb/pdbguide2.2/part_36.html
void SetName(const std::string &resname);
//! \brief Set the segment name of this residue (max four characters)
void SetSegName(const std::string &segname);
//! Set the residue number (in the sequence)
void SetNum(const unsigned int resnum);
void SetNum(const std::string resnum);
Expand All @@ -104,6 +106,8 @@ namespace OpenBabel {

//! \return The residue name
std::string GetName(void) const;
//! \return The residue segment name
std::string GetSegName(void) const;
//! \return The residue number (in the sequence)
int GetNum(void);
std::string GetNumString(void);
Expand Down Expand Up @@ -177,6 +181,7 @@ namespace OpenBabel {
unsigned int _reskey;//!< Residue key ID -- see SetResidueKeys()
std::string _resnum;//!< Residue number (i.e., in file) 23, 1B, etc.
std::string _resname;//!<Residue text name
std::string _segname;//!<Segment text name
char _insertioncode;//!<PBB insertion code

std::vector<bool> _hetatm;//!< Is a given atom a HETAM
Expand Down
27 changes: 22 additions & 5 deletions src/formats/pdbformat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,8 +514,9 @@ namespace OpenBabel

unsigned int i;
char buffer[BUFF_SIZE];
char type_name[10], padded_name[10];
char the_res[10];
char type_name[10] = {0,}, padded_name[10] = {0,};
char the_res[10] = {0,};
char segname[10] = {0,};
char the_chain = ' ';
const char *element_name;
int res_num;
Expand Down Expand Up @@ -688,6 +689,8 @@ namespace OpenBabel
snprintf(type_name,5,"%s",(char*)res->GetAtomID(atom).c_str());
the_chain = res->GetChain();

snprintf(segname,5,"%s", (char*)res->GetSegName().c_str());

//two char. elements are on position 13 and 14 one char. start at 14
if (strlen(OBElements::GetSymbol(atom->GetAtomicNum())) == 1)
{
Expand Down Expand Up @@ -724,6 +727,7 @@ namespace OpenBabel
type_name[4] = '\0';
res_num = 1;
the_insertioncode=' ';
strcpy(segname," ");
}

element_name = OBElements::GetSymbol(atom->GetAtomicNum());
Expand All @@ -745,7 +749,7 @@ namespace OpenBabel
occup = occup_fp->GetGenericValue();
}

snprintf(buffer, BUFF_SIZE, "%s%5d %-4s %-3s %c%4d%c %8.3f%8.3f%8.3f%6.2f 0.00 %2s%2s\n",
snprintf(buffer, BUFF_SIZE, "%s%5d %-4s %-3s %c%4d%c %8.3f%8.3f%8.3f%6.2f 0.00 %4s%2s%2s\n",
het?"HETATM":"ATOM ",
i,
type_name,
Expand All @@ -757,6 +761,7 @@ namespace OpenBabel
atom->GetY(),
atom->GetZ(),
occup,
segname,
element_name,
scharge);
ofs << buffer;
Expand Down Expand Up @@ -911,6 +916,15 @@ namespace OpenBabel
/* insertion code */
char insertioncode = sbuf.substr(27-6-1,1)[0];
if (' '==insertioncode) insertioncode=0;

/* segname */
string segname;
if (sbuf.size() > 67) {
segname = sbuf.substr(66,4);
if(segname == " ") { //unset should be empty string
segname = string();
}
}
/* element */
string element = " ";
if (sbuf.size() > 71)
Expand Down Expand Up @@ -1129,14 +1143,16 @@ namespace OpenBabel
|| res->GetName() != resname
|| res->GetNumString() != resnum
|| res->GetChain() != chain
|| res->GetInsertionCode() != insertioncode)
|| res->GetInsertionCode() != insertioncode
|| res->GetSegName() != segname)
{
vector<OBResidue*>::iterator ri;
for (res = mol.BeginResidue(ri) ; res ; res = mol.NextResidue(ri))
if (res->GetName() == resname
&& res->GetNumString() == resnum
&& static_cast<int>(res->GetChain()) == chain
&& static_cast<int>(res->GetInsertionCode()) == insertioncode) {
&& static_cast<int>(res->GetInsertionCode()) == insertioncode
&& res->GetSegName() == segname) {
if (insertioncode) fprintf(stderr,"I: identified residue wrt insertion code: '%c'\n",insertioncode);
break;
}
Expand All @@ -1147,6 +1163,7 @@ namespace OpenBabel
res->SetName(resname);
res->SetNum(resnum);
res->SetInsertionCode(insertioncode);
res->SetSegName(segname);
}
}

Expand Down
10 changes: 10 additions & 0 deletions src/residue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,11 @@ namespace OpenBabel
SetResidueKeys(_resname.c_str(), _reskey, _aakey);
}

void OBResidue::SetSegName(const string &name)
{
_segname = name;
}

void OBResidue::SetNum(const unsigned int resnum)
{
stringstream temp;
Expand Down Expand Up @@ -1037,6 +1042,11 @@ namespace OpenBabel
return _resname;
}

string OBResidue::GetSegName(void) const
{
return _segname;
}

std::string OBResidue::GetNumString(void)
{
return _resnum;
Expand Down
45 changes: 45 additions & 0 deletions test/testpdbformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,55 @@
import unittest

from testbabel import run_exec, executable, BaseTest
from testbindings import pybel

class TestPDBFormat(BaseTest):
"""A series of tests relating to PDB"""

def testSegname(self):
"""Test that different segments are put in different residues"""
self.pdbin = '''ATOM 102 N CYS A 16 59.916 27.715 54.719 1.00 30.93 AAAA N
ATOM 104 C CYS A 16 61.349 29.663 54.116 1.00 31.27 AAAA C
ATOM 105 O CYS A 16 62.398 30.296 54.175 1.00 31.42 AAAA O
ATOM 106 CB CYS A 16 62.233 27.349 54.045 1.00 30.95 AAAA C
ATOM 107 SG CYS A 16 62.584 25.823 54.921 1.00 31.06 AAAA S
ATOM 2492 N CYS A 16 46.752 17.445 54.719 1.00 30.93 B N
ATOM 2493 CA CYS A 16 45.412 16.879 54.750 1.00 31.03 B C
ATOM 2494 C CYS A 16 45.319 15.497 54.116 1.00 31.27 B C
ATOM 2495 O CYS A 16 44.270 14.864 54.175 1.00 31.42 B O
ATOM 2496 CB CYS A 16 44.435 17.811 54.045 1.00 30.95 B C
ATOM 2497 SG CYS A 16 44.084 19.337 54.921 1.00 31.06 B S
'''
self.pdbout = '''ATOM 1 N CYS A 16 59.916 27.715 54.719 1.00 0.00 AAAA N
ATOM 2 C CYS A 16 61.349 29.663 54.116 1.00 0.00 AAAA C
ATOM 3 O CYS A 16 62.398 30.296 54.175 1.00 0.00 AAAA O
ATOM 4 CB CYS A 16 62.233 27.349 54.045 1.00 0.00 AAAA C
ATOM 5 SG CYS A 16 62.584 25.823 54.921 1.00 0.00 AAAA S
ATOM 6 N CYS A 16 46.752 17.445 54.719 1.00 0.00 B N
ATOM 7 CA CYS A 16 45.412 16.879 54.750 1.00 0.00 B C
ATOM 8 C CYS A 16 45.319 15.497 54.116 1.00 0.00 B C
ATOM 9 O CYS A 16 44.270 14.864 54.175 1.00 0.00 B O
ATOM 10 CB CYS A 16 44.435 17.811 54.045 1.00 0.00 B C
ATOM 11 SG CYS A 16 44.084 19.337 54.921 1.00 0.00 B S '''

output, error = run_exec(self.pdbin,
"obabel -ipdb -opdb")

#pull out only atoms
outatoms = '\n'.join([line for line in output.split('\n') if line.startswith('ATOM')])
self.assertEqual(outatoms, self.pdbout)

#skip if bindings not present
if pybel:
mol = pybel.readstring('pdb',self.pdbin)
self.assertTrue(len(mol.residues) == 2)
cnts = {'AAAA':0,'B':0}
for a in mol.atoms:
r = a.OBAtom.GetResidue()
cnts[r.GetSegName().strip()] += 1
self.assertEqual(cnts['AAAA'],5)
self.assertEqual(cnts['B'],6)

def testInsertionCodes(self):
"""
Testing a PDB entry with insertion codes to distinguish residues
Expand Down

0 comments on commit 283cc84

Please sign in to comment.