"""This module handles the logic of converting a :py:class:`.PdbDataFile` to a
:py:class:`.PdbFile`"""
import math
from ..pdb.pdbfile import PdbFile, PdbRecord
from ..pdb.pdbdatafile import PdbDataFile
[docs]def pdb_file_from_pdb_data_file(data_file):
"""Takes a :py:class:`.PdbDataFile`, converts it to a :py:class:`.PdbFile`,
and returns it.
:param PdbDataFile data_file: The :py:class:`.PdbDataFile` to convert.
:rtype: :py:class:`.PdbFile`"""
if not isinstance(data_file, PdbDataFile):
raise TypeError(
"pdb_file_from_pdb_data_file can only convert PdbDataFiles"
)
pdb_file = PdbFile()
pdb_file._source = data_file
create_compnd_records(pdb_file, data_file)
create_atom_records(pdb_file, data_file)
create_atom_records(pdb_file, data_file, hetero=True)
create_conect_records(pdb_file, data_file)
return pdb_file
[docs]def create_compnd_records(pdb_file, data_file):
"""Takes a :py:class:`.PdbFile` and creates COMPND records in it based on
the data in the provided :py:class:`.PdbDataFile`
:param PdbFile pdb_file: the PDB File to update.
:param PdbDataFile data_file: The source Pdb Data File"""
lines = []
for compound in data_file.compounds():
segments = []
if "MOL_ID" in compound:
segments.append("MOL_ID: %s;" % str(compound["MOL_ID"]))
if "MOLECULE" in compound:
segments.append("MOLECULE: %s;" % str(compound["MOLECULE"]))
if "CHAIN" in compound:
segments.append("CHAIN: %s;" % ", ".join(compound["CHAIN"]))
if "SYNONYM" in compound:
segments.append("SYNONYM: %s;" % ", ".join(compound["SYNONYM"]))
if "EC" in compound:
segments.append("EC: %s;" % str(compound["EC"]))
if "ENGINEERED" in compound:
segments.append(
"ENGINEERED: %s;" % "YES" if compound["ENGINEERED"] else "NO"
)
for segment in segments:
if len(segment) <= 69:
lines.append(segment)
else:
chunks = segment.split(" ")[::-1]
growing_line = ""
while chunks:
chunk = chunks.pop()
if len(growing_line + chunk) > 69:
lines.append(growing_line)
growing_line = chunk + " "
else:
growing_line += chunk + " "
if not chunks:
lines.append(growing_line)
for index, line in enumerate(lines):
pdb_file.add_record(PdbRecord("COMPND %s%s" % (
str(index + 1).rjust(3) + " " if index != 0 else " ",
line
)))
[docs]def create_atom_records(pdb_file, data_file, hetero=False):
"""Takes a :py:class:`.PdbFile` and creates ATOM and HETATM records in it
based on the data in the provided :py:class:`.PdbDataFile`
:param PdbFile pdb_file: the PDB File to update.
:param PdbDataFile data_file: The source Pdb Data File
:param bool hetero: if True, the function will create HETATM records, and\
if False, ATOM records will be created. Default is False."""
atoms = data_file.heteroatoms() if hetero else data_file.atoms()
for atom in atoms:
record_fragments = []
record_fragments.append("HETATM" if hetero else "ATOM ")
record_fragments.append("%5i" % atom["atom_id"] + " ")
record_fragments.append("%-4s" % atom["atom_name"][:4])
record_fragments.append(atom["alt_loc"][0] if atom["alt_loc"] else " ")
record_fragments.append(
atom["residue_name"][0:3] + " " if atom["residue_name"] else " "
)
record_fragments.append(
atom["chain_id"][0] if atom["chain_id"] else " "
)
record_fragments.append(
("%4i" % atom["residue_id"]) if atom["residue_id"] else " "
)
record_fragments.append(
atom["insert_code"][0] + " " if atom["insert_code"] else " "
)
record_fragments.append(_number_to_8_char_string(atom["x"]))
record_fragments.append(_number_to_8_char_string(atom["y"]))
record_fragments.append(_number_to_8_char_string(atom["z"]))
record_fragments.append(_number_to_6_char_string(atom["occupancy"]))
record_fragments.append(
_number_to_6_char_string(atom["temperature_factor"])
)
record_fragments.append(" " * 10)
record_fragments.append("%-2s" % atom["element"])
record_fragments.append(
("%-2i" % atom["charge"]) if atom["charge"] else " "
)
pdb_file.add_record(PdbRecord("".join(record_fragments)))
[docs]def create_conect_records(pdb_file, data_file):
"""Takes a :py:class:`.PdbFile` and creates CONECT records in it based on
the data in the provided :py:class:`.PdbDataFile`
:param PdbFile pdb_file: the PDB File to update.
:param PdbDataFile data_file: The source Pdb Data File"""
for connection in data_file.connections():
record_count = math.ceil(len(connection["bonded_atoms"]) / 4)
for n in range(record_count):
pdb_file.add_record(PdbRecord("CONECT%5i%5s%5s%5s%5s" % (
connection["atom_id"],
str(connection["bonded_atoms"][(n * 4)])\
if (n * 4) < len(connection["bonded_atoms"]) else "",
str(connection["bonded_atoms"][(n * 4) + 1])
if (n * 4) + 1 < len(connection["bonded_atoms"]) else "",
str(connection["bonded_atoms"][(n * 4) + 2])
if (n * 4) + 2 < len(connection["bonded_atoms"]) else "",
str(connection["bonded_atoms"][(n * 4) + 3])
if (n * 4) + 3 < len(connection["bonded_atoms"]) else ""
)))
def _number_to_8_char_string(number):
return _number_to_n_char_string(number, 8)
def _number_to_6_char_string(number):
return _number_to_n_char_string(number, 6)
def _number_to_n_char_string(number, n):
if number is None:
return " " * n
else:
number = round(number, n)
int_component = str(int(number))
if number < 0 and int_component[0] != "-":
int_component = "-" + int_component
float_component = number - int(number)
if len(int_component) >= 6 or float_component == 0 or "e" in str(float_component):
float_component = ".0"
else:
float_component = str(round(float_component, (n - 1) - len(int_component)))
if float_component[0] == "1":
float_component = ".0"
int_component = str(int(int_component) + 1)
else:
float_component = "." + float_component.split(".")[-1]
return (int_component + float_component).ljust(n)