Source code for pestifer.objs.ter
# Author: Cameron F. Abrams, <cfa22@drexel.edu>
"""
A class for handling TER records in PDB files.
Why is this necessary? TER records are used to indicate
'breaks' in the list of ATOM records in a PDB file to signify
physical discontinuity of the amino acid chain(s). The problem
we have to solve is that sometimes a TER record gets a unique
atom serial number, even though they are not atoms. So in a list
of atoms the sequence of serial numbers can be discontinuous.
This is not a problem per se, *but* when VMD reads in a PDB file
it ignores both TER records *and* the explicit atom serial numbers.
Pidibble by default does not, so to make the atom records
VMD-compliant, we have to adjust atom serial numbers, and to do
that we have to know where any non-empty TER records are.
"""
import logging
from pidibble.pdbrecord import PDBRecord, PDBRecordDict
from pydantic import Field
from typing import ClassVar
from .resid import ResID
from ..core.baseobj import BaseObj, BaseObjList
logger = logging.getLogger(__name__)
[docs]
class Ter(BaseObj):
"""
A class for handing TER records in PDB files
"""
_optional_fields = {'serial', 'resname', 'chainID', 'resid'}
"""
Optional attributes for a Ter object. Ters can be empty.
These attributes can be provided when creating a Ter object.
- ``serial``: The serial number of the TER record.
- ``resname``: The residue name of the TER record.
- ``chainID``: The chain ID of the TER record.
- ``resid``: The residue ID of the TER record.
"""
serial: int | None = Field(None, description="Serial number of the TER record")
resname: str | None = Field(None, description="Residue name of the TER record")
chainID: str | None = Field(None, description="Chain ID of the TER record")
resid: ResID | None = Field(None, description="Residue ID of the TER record")
_yaml_header: ClassVar[str] ='terminals'
"""
YAML header for Ter objects.
This header is used to identify Ter objects in YAML files.
"""
_objcat: ClassVar[str] = 'seq'
"""
Category of the Ter object.
This categorization is used to group Ter objects in the object manager.
"""
_PDB_keyword: ClassVar[str] = 'TER'
"""
The PDB keyword for TER records.
This keyword is used to identify TER records in PDB files.
"""
@classmethod
def _adapt(cls, *args, **kwargs) -> dict:
"""
Adapts the input to a dictionary format suitable for Ter instantiations.
This method is used to convert various input types into a dictionary of parameters.
"""
if args and isinstance(args[0], PDBRecord):
pdbrecord: PDBRecord = args[0]
assert pdbrecord.key == 'TER'
# logger.debug(f'serial "{pdbrecord.serial}" ({type(pdbrecord.serial)})')
# logger.debug(f'Adapting PDBRecord {str(pdbrecord)} to Ter')
if not pdbrecord.serial:
# this TER is empty
# logger.debug(f'Empty ter')
input_dict = {
'serial': None,
'resname': None,
'chainID': None,
'resid': None
}
return input_dict
else:
input_dict = {
'serial': pdbrecord.serial,
'resname': pdbrecord.residue.resName,
'chainID': pdbrecord.residue.chainID,
'resid': ResID(resseqnum=pdbrecord.residue.seqNum, insertion=pdbrecord.residue.iCode)
}
return input_dict
return super()._adapt(*args, **kwargs)
[docs]
class TerList(BaseObjList[Ter]):
# _has_serials: ClassVar[bool] = False
[docs]
def describe(self):
return f'<TerList with {len(self)} TER records, has_serials={self._has_serials}>'
[docs]
@classmethod
def from_pdb(cls, parsed: PDBRecordDict, model_id = None) -> "TerList":
"""
Create a TerList from parsed PDB data.
Parameters
----------
parsed : PDBRecordDict
A dictionary containing parsed PDB records.
model_id : int, optional
The model ID to filter the TER records by. If None, all models are included.
Returns
-------
TerList
A new TerList instance containing Ter objects created from the PDB data.
"""
if Ter._PDB_keyword not in parsed or not parsed[Ter._PDB_keyword]:
return cls([])
# logger.debug(f'Reading from {[repr(p) for p in parsed[Ter._PDB_keyword]]}')
L = cls([Ter(p) for p in parsed[Ter._PDB_keyword] if (model_id is None or p.model == model_id)])
return L
[docs]
def has_serials(self):
return any(x.serial is not None for x in self)