Spaces:
No application file
No application file
# Copyright (C) 2002, Thomas Hamelryck ([email protected]) | |
# This code is part of the Biopython distribution and governed by its | |
# license. Please see the LICENSE file that should have been included | |
# as part of this package. | |
"""Consumer class that builds a Structure object. | |
This is used by the PDBParser and MMCIFparser classes. | |
""" | |
import warnings | |
# SMCRA hierarchy | |
from Bio.PDB.Structure import Structure | |
from Bio.PDB.Model import Model | |
from Bio.PDB.Chain import Chain | |
from Bio.PDB.Residue import Residue, DisorderedResidue | |
from Bio.PDB.Atom import Atom, DisorderedAtom | |
from Bio.PDB.PDBExceptions import PDBConstructionException | |
from Bio.PDB.PDBExceptions import PDBConstructionWarning | |
class StructureBuilder: | |
"""Deals with constructing the Structure object. | |
The StructureBuilder class is used by the PDBParser classes to | |
translate a file to a Structure object. | |
""" | |
def __init__(self): | |
"""Initialize the class.""" | |
self.line_counter = 0 | |
self.header = {} | |
def _is_completely_disordered(self, residue): | |
"""Return 1 if all atoms in the residue have a non blank altloc (PRIVATE).""" | |
atom_list = residue.get_unpacked_list() | |
for atom in atom_list: | |
altloc = atom.get_altloc() | |
if altloc == " ": | |
return 0 | |
return 1 | |
# Public methods called by the Parser classes | |
def set_header(self, header): | |
"""Set header.""" | |
self.header = header | |
def set_line_counter(self, line_counter): | |
"""Tracks line in the PDB file that is being parsed. | |
Arguments: | |
- line_counter - int | |
""" | |
self.line_counter = line_counter | |
def init_structure(self, structure_id): | |
"""Initialize a new Structure object with given id. | |
Arguments: | |
- id - string | |
""" | |
self.structure = Structure(structure_id) | |
def init_model(self, model_id, serial_num=None): | |
"""Create a new Model object with given id. | |
Arguments: | |
- id - int | |
- serial_num - int | |
""" | |
self.model = Model(model_id, serial_num) | |
self.structure.add(self.model) | |
def init_chain(self, chain_id): | |
"""Create a new Chain object with given id. | |
Arguments: | |
- chain_id - string | |
""" | |
if self.model.has_id(chain_id): | |
self.chain = self.model[chain_id] | |
warnings.warn( | |
"WARNING: Chain %s is discontinuous at line %i." | |
% (chain_id, self.line_counter), | |
PDBConstructionWarning, | |
) | |
else: | |
self.chain = Chain(chain_id) | |
self.model.add(self.chain) | |
def init_seg(self, segid): | |
"""Flag a change in segid. | |
Arguments: | |
- segid - string | |
""" | |
self.segid = segid | |
def init_residue(self, resname, field, resseq, icode): | |
"""Create a new Residue object. | |
Arguments: | |
- resname - string, e.g. "ASN" | |
- field - hetero flag, "W" for waters, "H" for | |
hetero residues, otherwise blank. | |
- resseq - int, sequence identifier | |
- icode - string, insertion code | |
""" | |
if field != " ": | |
if field == "H": | |
# The hetero field consists of H_ + the residue name (e.g. H_FUC) | |
field = "H_" + resname | |
res_id = (field, resseq, icode) | |
if field == " ": | |
if self.chain.has_id(res_id): | |
# There already is a residue with the id (field, resseq, icode). | |
# This only makes sense in the case of a point mutation. | |
warnings.warn( | |
"WARNING: Residue ('%s', %i, '%s') redefined at line %i." | |
% (field, resseq, icode, self.line_counter), | |
PDBConstructionWarning, | |
) | |
duplicate_residue = self.chain[res_id] | |
if duplicate_residue.is_disordered() == 2: | |
# The residue in the chain is a DisorderedResidue object. | |
# So just add the last Residue object. | |
if duplicate_residue.disordered_has_id(resname): | |
# The residue was already made | |
self.residue = duplicate_residue | |
duplicate_residue.disordered_select(resname) | |
else: | |
# Make a new residue and add it to the already | |
# present DisorderedResidue | |
new_residue = Residue(res_id, resname, self.segid) | |
duplicate_residue.disordered_add(new_residue) | |
self.residue = duplicate_residue | |
return | |
else: | |
if resname == duplicate_residue.resname: | |
warnings.warn( | |
"WARNING: Residue ('%s', %i, '%s','%s') already defined " | |
"with the same name at line %i." | |
% (field, resseq, icode, resname, self.line_counter), | |
PDBConstructionWarning, | |
) | |
self.residue = duplicate_residue | |
return | |
# Make a new DisorderedResidue object and put all | |
# the Residue objects with the id (field, resseq, icode) in it. | |
# These residues each should have non-blank altlocs for all their atoms. | |
# If not, the PDB file probably contains an error. | |
if not self._is_completely_disordered(duplicate_residue): | |
# if this exception is ignored, a residue will be missing | |
self.residue = None | |
raise PDBConstructionException( | |
"Blank altlocs in duplicate residue %s ('%s', %i, '%s')" | |
% (resname, field, resseq, icode) | |
) | |
self.chain.detach_child(res_id) | |
new_residue = Residue(res_id, resname, self.segid) | |
disordered_residue = DisorderedResidue(res_id) | |
self.chain.add(disordered_residue) | |
disordered_residue.disordered_add(duplicate_residue) | |
disordered_residue.disordered_add(new_residue) | |
self.residue = disordered_residue | |
return | |
self.residue = Residue(res_id, resname, self.segid) | |
self.chain.add(self.residue) | |
def init_atom( | |
self, | |
name, | |
coord, | |
b_factor, | |
occupancy, | |
altloc, | |
fullname, | |
serial_number=None, | |
element=None, | |
pqr_charge=None, | |
radius=None, | |
is_pqr=False, | |
): | |
"""Create a new Atom object. | |
Arguments: | |
- name - string, atom name, e.g. CA, spaces should be stripped | |
- coord - Numeric array (Float0, size 3), atomic coordinates | |
- b_factor - float, B factor | |
- occupancy - float | |
- altloc - string, alternative location specifier | |
- fullname - string, atom name including spaces, e.g. " CA " | |
- element - string, upper case, e.g. "HG" for mercury | |
- pqr_charge - float, atom charge (PQR format) | |
- radius - float, atom radius (PQR format) | |
- is_pqr - boolean, flag to specify if a .pqr file is being parsed | |
""" | |
residue = self.residue | |
# if residue is None, an exception was generated during | |
# the construction of the residue | |
if residue is None: | |
return | |
# First check if this atom is already present in the residue. | |
# If it is, it might be due to the fact that the two atoms have atom | |
# names that differ only in spaces (e.g. "CA.." and ".CA.", | |
# where the dots are spaces). If that is so, use all spaces | |
# in the atom name of the current atom. | |
if residue.has_id(name): | |
duplicate_atom = residue[name] | |
# atom name with spaces of duplicate atom | |
duplicate_fullname = duplicate_atom.get_fullname() | |
if duplicate_fullname != fullname: | |
# name of current atom now includes spaces | |
name = fullname | |
warnings.warn( | |
"Atom names %r and %r differ only in spaces at line %i." | |
% (duplicate_fullname, fullname, self.line_counter), | |
PDBConstructionWarning, | |
) | |
if not is_pqr: | |
self.atom = Atom( | |
name, | |
coord, | |
b_factor, | |
occupancy, | |
altloc, | |
fullname, | |
serial_number, | |
element, | |
) | |
elif is_pqr: | |
self.atom = Atom( | |
name, | |
coord, | |
None, | |
None, | |
altloc, | |
fullname, | |
serial_number, | |
element, | |
pqr_charge, | |
radius, | |
) | |
if altloc != " ": | |
# The atom is disordered | |
if residue.has_id(name): | |
# Residue already contains this atom | |
duplicate_atom = residue[name] | |
if duplicate_atom.is_disordered() == 2: | |
duplicate_atom.disordered_add(self.atom) | |
else: | |
# This is an error in the PDB file: | |
# a disordered atom is found with a blank altloc | |
# Detach the duplicate atom, and put it in a | |
# DisorderedAtom object together with the current | |
# atom. | |
residue.detach_child(name) | |
disordered_atom = DisorderedAtom(name) | |
residue.add(disordered_atom) | |
disordered_atom.disordered_add(self.atom) | |
disordered_atom.disordered_add(duplicate_atom) | |
residue.flag_disordered() | |
warnings.warn( | |
"WARNING: disordered atom found with blank altloc before " | |
"line %i.\n" % self.line_counter, | |
PDBConstructionWarning, | |
) | |
else: | |
# The residue does not contain this disordered atom | |
# so we create a new one. | |
disordered_atom = DisorderedAtom(name) | |
residue.add(disordered_atom) | |
# Add the real atom to the disordered atom, and the | |
# disordered atom to the residue | |
disordered_atom.disordered_add(self.atom) | |
residue.flag_disordered() | |
else: | |
# The atom is not disordered | |
residue.add(self.atom) | |
def set_anisou(self, anisou_array): | |
"""Set anisotropic B factor of current Atom.""" | |
self.atom.set_anisou(anisou_array) | |
def set_siguij(self, siguij_array): | |
"""Set standard deviation of anisotropic B factor of current Atom.""" | |
self.atom.set_siguij(siguij_array) | |
def set_sigatm(self, sigatm_array): | |
"""Set standard deviation of atom position of current Atom.""" | |
self.atom.set_sigatm(sigatm_array) | |
def get_structure(self): | |
"""Return the structure.""" | |
# first sort everything | |
# self.structure.sort() | |
# Add the header dict | |
self.structure.header = self.header | |
return self.structure | |
def set_symmetry(self, spacegroup, cell): | |
"""Set symmetry.""" | |
pass | |