Beispiel #1
0
def read_mmcif_to_biopython(path):
    """Read in mmcif protein structure and report its Biopython structure

    Args:
        path (str): Path to the mmcif file.

    Raises:
        ValueError: In case _atom_site table is not present in the file.

    Returns:
        Bio.PDB.Structure.Structure: BioPython PDB structure
    """
    if not os.path.isfile(path):
        raise IOError("File {} not found".format(path))

    structure_builder = StructureBuilder()
    parsed = MMCIF2Dict().parse(path)

    file_name = os.path.basename(path).split(".")[0]
    structure_id = next((x for x in parsed), file_name).lower()
    structure_builder.init_structure(structure_id)

    try:
        perceived_atom_site = list(parsed.values())[0]["_atom_site"]
        _atom_site = _trim_models(perceived_atom_site)
        _parse_atom_site_biopython(_atom_site, structure_builder)
    except KeyError:
        raise ValueError("The cif file does not contain _atom_site record")

    return structure_builder.get_structure()
Beispiel #2
0
def pdb_writer(graph, results, dir_name="templates"):
    PDB_CODE = graph.protein_name
    structure = graph.structure

    path = "%s/%s/" % (dir_name, PDB_CODE)
    if len(results) > 0:
        if not os.path.exists(path):
            os.makedirs(path)
    else:
        print 'No results'
        return

    top = min(NUM_OF_RESULTS_TO_SAVE, len(results))
    i = 0
    for key, value in results[:top]:
        # TODO - save as different chains
        sb = StructureBuilder()
        sb.init_structure(PDB_CODE)
        sb.init_model(0)
        sb.init_chain('X')
        for node_id in key[0]:
            for res in graph.nodes[node_id].residues:
                sb.init_seg(res.get_segid())
                sb.init_residue(res.get_resname(),
                                res.get_id()[0],
                                res.get_id()[1],
                                res.get_id()[2])
                for atom in res:
                    sb.init_atom(atom.get_name(), atom.get_coord(),
                                 atom.get_bfactor(), atom.get_occupancy(),
                                 atom.get_altloc(), atom.get_fullname())

        sb.init_chain('Y')
        for node_id in key[1]:
            for res in graph.nodes[node_id].residues:
                sb.init_residue(res.get_resname(),
                                res.get_id()[0],
                                res.get_id()[1],
                                res.get_id()[2])
                for atom in res:
                    sb.init_atom(atom.get_name(), atom.get_coord(),
                                 atom.get_bfactor(), atom.get_occupancy(),
                                 atom.get_altloc(), atom.get_fullname())
        filename = path + "interface%d.pdb" % (i)
        io = PDBIO()
        io.set_structure(sb.get_structure())
        io.save(filename)
        i += 1

    return 1
def residues_to_struct(name_residues_list, structID):
    """
    Build a structure from a list of (chain name, residues) - each as
    a chain.
    """
    
    builder = StructureBuilder()
    builder.init_structure(structID)
    builder.init_model(0)

    for (name, residues) in name_residues_list:
        builder.init_chain(name)
        for res in residues:
            builder.chain.add(res)

    return builder.get_structure()
Beispiel #4
0
    def coarse_grain(self, cg_type="CA_TRACE"):
        """ 
            Reduces the protein structure complexity to a few (pseudo-)atoms per residue.

            Parameters:
              - cg_type:      CA_TRACE (Ca-only) [Default]
                              ENCAD_3P (CA, O, SC Beads)
                              MARTINI (CA, O, SC Beads)
            
            Returns a new structure object.
        """

        # Import CG Types

        import CG_Models

        CG_Library = {
            "CA_TRACE": CG_Models.CA_TRACE,
            "ENCAD_3P": CG_Models.ENCAD_3P,
            "MARTINI": CG_Models.MARTINI
        }

        CG_Method = CG_Library[cg_type]

        # Creates a brand new structure object
        from Bio.PDB.StructureBuilder import StructureBuilder

        structure_builder = StructureBuilder()

        cg_id = "CG_" + self.id
        structure_builder.init_structure(cg_id)
        structure_builder.init_seg(' ')  # Empty SEGID

        for model in self:
            structure_builder.init_model(model.id)

            for chain in model:
                structure_builder.init_chain(chain.id)
                cur_chain = structure_builder.chain

                for residue in chain:
                    cg_residue = CG_Method(residue)
                    cur_chain.add(cg_residue)

        cg_structure = structure_builder.get_structure()

        return cg_structure
Beispiel #5
0
class XBGFParser:
    def __init__(self, PERMISSIVE=1, structure_builder=None):
        if structure_builder != None:
            self.structure_builder = structure_builder
        else:
            self.structure_builder = StructureBuilder()
        self.PERMISSIVE = PERMISSIVE

    # public interface

    def parse(self, id, file):
        self.structure_builder.init_structure(id)
        if isinstance(file, basestring):
            file=open(file)
        self.charges = dict()
        self.chain_suffix = 0
        self._parse(file.readlines())
        self.structure = self.structure_builder.get_structure()
        return self._process_structure()

    # private methods

    def _parse(self, lines):
        self.structure_builder.init_model(0)
        self.structure_builder.init_seg("")
        self.current_chain_id = None
        self.current_residue_id = None
        self.current_resname = None
        for i in range(0, len(lines)):
            self.line_counter = i + 1
            self.structure_builder.set_line_counter(self.line_counter)
            line = lines[i]
            if line[0:6] == 'ATOM  ':
                self._update_atom(line)

    def _update_chain(self, line):
        chain_id = self._extract_chain(line)
        if self.current_chain_id != chain_id:
            try:
                self.structure_builder.init_chain(chain_id)
                self.current_chain_id = chain_id
                self.current_residue_id = None
                self.current_resname = None
            except PDBConstructionException, message:
                self._handle_PDB_exception(message) 
Beispiel #6
0
class XBGFParser:
    def __init__(self, PERMISSIVE=1, structure_builder=None):
        if structure_builder != None:
            self.structure_builder = structure_builder
        else:
            self.structure_builder = StructureBuilder()
        self.PERMISSIVE = PERMISSIVE

    # public interface

    def parse(self, id, file):
        self.structure_builder.init_structure(id)
        if isinstance(file, basestring):
            file = open(file)
        self.charges = dict()
        self.chain_suffix = 0
        self._parse(file.readlines())
        self.structure = self.structure_builder.get_structure()
        return self._process_structure()

    # private methods

    def _parse(self, lines):
        self.structure_builder.init_model(0)
        self.structure_builder.init_seg("")
        self.current_chain_id = None
        self.current_residue_id = None
        self.current_resname = None
        for i in range(0, len(lines)):
            self.line_counter = i + 1
            self.structure_builder.set_line_counter(self.line_counter)
            line = lines[i]
            if line[0:6] == 'ATOM  ':
                self._update_atom(line)

    def _update_chain(self, line):
        chain_id = self._extract_chain(line)
        if self.current_chain_id != chain_id:
            try:
                self.structure_builder.init_chain(chain_id)
                self.current_chain_id = chain_id
                self.current_residue_id = None
                self.current_resname = None
            except PDBConstructionException, message:
                self._handle_PDB_exception(message)
Beispiel #7
0
class FastMMCIFParser(object):
    """Parse an MMCIF file and return a Structure object."""

    def __init__(self, structure_builder=None, QUIET=False):
        """Create a FastMMCIFParser object.

        The mmCIF parser calls a number of standard methods in an aggregated
        StructureBuilder object. Normally this object is instanciated by the
        parser object itself, but if the user provides his/her own
        StructureBuilder object, the latter is used instead.

        The main difference between this class and the regular MMCIFParser is
        that only 'ATOM' and 'HETATM' lines are parsed here. Use if you are
        interested only in coordinate information.

        Arguments:
         - structure_builder - an optional user implemented StructureBuilder class.
         - QUIET - Evaluated as a Boolean. If true, warnings issued in constructing
           the SMCRA data will be suppressed. If false (DEFAULT), they will be shown.
           These warnings might be indicative of problems in the mmCIF file!

        """
        if structure_builder is not None:
            self._structure_builder = structure_builder
        else:
            self._structure_builder = StructureBuilder()

        self.line_counter = 0
        self.build_structure = None
        self.QUIET = bool(QUIET)

    # Public methods

    def get_structure(self, structure_id, filename):
        """Return the structure.

        Arguments:
         - structure_id - string, the id that will be used for the structure
         - filename - name of the mmCIF file OR an open filehandle

        """
        with warnings.catch_warnings():
            if self.QUIET:
                warnings.filterwarnings("ignore", category=PDBConstructionWarning)
            with as_handle(filename) as handle:
                self._build_structure(structure_id, handle)

        return self._structure_builder.get_structure()

    # Private methods

    def _build_structure(self, structure_id, filehandle):

        # two special chars as placeholders in the mmCIF format
        # for item values that cannot be explicitly assigned
        # see: pdbx/mmcif syntax web page
        _unassigned = set(('.', '?'))

        # Read only _atom_site. and atom_site_anisotrop entries
        read_atom, read_aniso = False, False
        _fields, _records = [], []
        _anisof, _anisors = [], []
        for line in filehandle:
            if line.startswith('_atom_site.'):
                read_atom = True
                _fields.append(line.strip())
            elif line.startswith('_atom_site_anisotrop.'):
                read_aniso = True
                _anisof.append(line.strip())
            elif read_atom and line.startswith('#'):
                read_atom = False
            elif read_aniso and line.startswith('#'):
                read_aniso = False
            elif read_atom:
                _records.append(line.strip())
            elif read_aniso:
                _anisors.append(line.strip())

        # Dumping the shlex module here since this particular
        # category should be rather straightforward.
        # Quite a performance boost..
        _record_tbl = zip(*map(str.split, _records))
        _anisob_tbl = zip(*map(str.split, _anisors))

        mmcif_dict = dict(zip(_fields, _record_tbl))
        mmcif_dict.update(dict(zip(_anisof, _anisob_tbl)))

        # Build structure object
        atom_id_list = mmcif_dict["_atom_site.label_atom_id"]
        residue_id_list = mmcif_dict["_atom_site.label_comp_id"]

        try:
            element_list = mmcif_dict["_atom_site.type_symbol"]
        except KeyError:
            element_list = None

        chain_id_list = mmcif_dict["_atom_site.auth_asym_id"]

        x_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_x"]]
        y_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_y"]]
        z_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_z"]]
        alt_list = mmcif_dict["_atom_site.label_alt_id"]
        icode_list = mmcif_dict["_atom_site.pdbx_PDB_ins_code"]
        b_factor_list = mmcif_dict["_atom_site.B_iso_or_equiv"]
        occupancy_list = mmcif_dict["_atom_site.occupancy"]
        fieldname_list = mmcif_dict["_atom_site.group_PDB"]

        try:
            serial_list = [int(n) for n in mmcif_dict["_atom_site.pdbx_PDB_model_num"]]
        except KeyError:
            # No model number column
            serial_list = None
        except ValueError:
            # Invalid model number (malformed file)
            raise PDBConstructionException("Invalid model number")

        try:
            aniso_u11 = mmcif_dict["_atom_site_anisotrop.U[1][1]"]
            aniso_u12 = mmcif_dict["_atom_site_anisotrop.U[1][2]"]
            aniso_u13 = mmcif_dict["_atom_site_anisotrop.U[1][3]"]
            aniso_u22 = mmcif_dict["_atom_site_anisotrop.U[2][2]"]
            aniso_u23 = mmcif_dict["_atom_site_anisotrop.U[2][3]"]
            aniso_u33 = mmcif_dict["_atom_site_anisotrop.U[3][3]"]
            aniso_flag = 1
        except KeyError:
            # no anisotropic B factors
            aniso_flag = 0

        # if auth_seq_id is present, we use this.
        # Otherwise label_seq_id is used.
        if "_atom_site.auth_seq_id" in mmcif_dict:
            seq_id_list = mmcif_dict["_atom_site.auth_seq_id"]
        else:
            seq_id_list = mmcif_dict["_atom_site.label_seq_id"]

        # Now loop over atoms and build the structure
        current_chain_id = None
        current_residue_id = None
        current_resname = None
        structure_builder = self._structure_builder
        structure_builder.init_structure(structure_id)
        structure_builder.init_seg(" ")

        # Historically, Biopython PDB parser uses model_id to mean array index
        # so serial_id means the Model ID specified in the file
        current_model_id = -1
        current_serial_id = -1
        for i in range(0, len(atom_id_list)):

            # set the line_counter for 'ATOM' lines only and not
            # as a global line counter found in the PDBParser()
            # this number should match the '_atom_site.id' index in the MMCIF
            structure_builder.set_line_counter(i)

            x = x_list[i]
            y = y_list[i]
            z = z_list[i]
            resname = residue_id_list[i]
            chainid = chain_id_list[i]
            altloc = alt_list[i]
            if altloc in _unassigned:
                altloc = " "
            int_resseq = int(seq_id_list[i])
            icode = icode_list[i]
            if icode in _unassigned:
                icode = " "
            name = atom_id_list[i].strip('"')  # Remove occasional " from quoted atom names (e.g. xNA)

            # occupancy & B factor
            try:
                tempfactor = float(b_factor_list[i])
            except ValueError:
                raise PDBConstructionException("Invalid or missing B factor")

            try:
                occupancy = float(occupancy_list[i])
            except ValueError:
                raise PDBConstructionException("Invalid or missing occupancy")

            fieldname = fieldname_list[i]
            if fieldname == "HETATM":
                hetatm_flag = "H"
            else:
                hetatm_flag = " "

            resseq = (hetatm_flag, int_resseq, icode)

            if serial_list is not None:
                # model column exists; use it
                serial_id = serial_list[i]
                if current_serial_id != serial_id:
                    # if serial changes, update it and start new model
                    current_serial_id = serial_id
                    current_model_id += 1
                    structure_builder.init_model(current_model_id, current_serial_id)
                    current_chain_id = None
                    current_residue_id = None
                    current_resname = None
            else:
                # no explicit model column; initialize single model
                structure_builder.init_model(current_model_id)

            if current_chain_id != chainid:
                current_chain_id = chainid
                structure_builder.init_chain(current_chain_id)
                current_residue_id = None
                current_resname = None

            if current_residue_id != resseq or current_resname != resname:
                current_residue_id = resseq
                current_resname = resname
                structure_builder.init_residue(resname, hetatm_flag, int_resseq, icode)

            coord = numpy.array((x, y, z), 'f')
            element = element_list[i] if element_list else None
            structure_builder.init_atom(name, coord, tempfactor, occupancy, altloc,
                                        name, element=element)
            if aniso_flag == 1 and i < len(aniso_u11):
                u = (aniso_u11[i], aniso_u12[i], aniso_u13[i],
                     aniso_u22[i], aniso_u23[i], aniso_u33[i])
                mapped_anisou = [float(_) for _ in u]
                anisou_array = numpy.array(mapped_anisou, 'f')
                structure_builder.set_anisou(anisou_array)
Beispiel #8
0
class PDBParser(object):
    """
    Parse a PDB file and return a Structure object.
    """
    def __init__(self,
                 PERMISSIVE=True,
                 get_header=False,
                 structure_builder=None,
                 QUIET=False):
        """
        The PDB parser call a number of standard methods in an aggregated
        StructureBuilder object. Normally this object is instanciated by the
        PDBParser object itself, but if the user provides his own StructureBuilder
        object, the latter is used instead.

        Arguments:
        
        o PERMISSIVE - Evaluated as a Boolean. If false, exceptions in
        constructing the SMCRA data structure are fatal. If true (DEFAULT),
        the exceptions are caught, but some residues or atoms will be missing.
        THESE EXCEPTIONS ARE DUE TO PROBLEMS IN THE PDB FILE!.

        o structure_builder - an optional user implemented StructureBuilder class. 

        o QUIET - Evaluated as a Boolean. If true, warnings issued in constructing
        the SMCRA data will be supressed. If false (DEFAULT), they will be shown.
        These warnings might be indicative of problems in the PDB file!        
        """
        if structure_builder != None:
            self.structure_builder = structure_builder
        else:
            self.structure_builder = StructureBuilder()
        self.header = None
        self.trailer = None
        self.line_counter = 0
        self.PERMISSIVE = bool(PERMISSIVE)
        self.QUIET = bool(QUIET)

    # Public methods

    def get_structure(self, id, file):
        """Return the structure.

        Arguments:
        o id - string, the id that will be used for the structure
        o file - name of the PDB file OR an open filehandle
        """

        if self.QUIET:
            warning_list = warnings.filters[:]
            warnings.filterwarnings('ignore', category=PDBConstructionWarning)

        self.header = None
        self.trailer = None
        # Make a StructureBuilder instance (pass id of structure as parameter)
        self.structure_builder.init_structure(id)

        with as_handle(file) as handle:
            self._parse(handle.readlines())

        self.structure_builder.set_header(self.header)
        # Return the Structure instance
        structure = self.structure_builder.get_structure()

        if self.QUIET:
            warnings.filters = warning_list

        return structure

    def get_header(self):
        "Return the header."
        return self.header

    def get_trailer(self):
        "Return the trailer."
        return self.trailer

    # Private methods

    def _parse(self, header_coords_trailer):
        "Parse the PDB file."
        # Extract the header; return the rest of the file
        self.header, coords_trailer = self._get_header(header_coords_trailer)
        # Parse the atomic data; return the PDB file trailer
        self.trailer = self._parse_coordinates(coords_trailer)

    def _get_header(self, header_coords_trailer):
        "Get the header of the PDB file, return the rest."
        structure_builder = self.structure_builder
        i = 0
        for i in range(0, len(header_coords_trailer)):
            structure_builder.set_line_counter(i + 1)
            line = header_coords_trailer[i]
            record_type = line[0:6]
            if (record_type == 'ATOM  ' or record_type == 'HETATM'
                    or record_type == 'MODEL '):
                break
        header = header_coords_trailer[0:i]
        # Return the rest of the coords+trailer for further processing
        self.line_counter = i
        coords_trailer = header_coords_trailer[i:]
        header_dict = _parse_pdb_header_list(header)
        return header_dict, coords_trailer

    def _parse_coordinates(self, coords_trailer):
        "Parse the atomic data in the PDB file."
        local_line_counter = 0
        structure_builder = self.structure_builder
        current_model_id = 0
        # Flag we have an open model
        model_open = 0
        current_chain_id = None
        current_segid = None
        current_residue_id = None
        current_resname = None
        for i in range(0, len(coords_trailer)):
            line = coords_trailer[i]
            record_type = line[0:6]
            global_line_counter = self.line_counter + local_line_counter + 1
            structure_builder.set_line_counter(global_line_counter)
            if (record_type == 'ATOM  ' or record_type == 'HETATM'):
                # Initialize the Model - there was no explicit MODEL record
                if not model_open:
                    structure_builder.init_model(current_model_id)
                    current_model_id += 1
                    model_open = 1
                fullname = line[12:16]
                # get rid of whitespace in atom names
                split_list = fullname.split()
                if len(split_list) != 1:
                    # atom name has internal spaces, e.g. " N B ", so
                    # we do not strip spaces
                    name = fullname
                else:
                    # atom name is like " CA ", so we can strip spaces
                    name = split_list[0]
                altloc = line[16:17]
                resname = line[17:20]
                chainid = line[21:22]
                try:
                    serial_number = int(line[6:11])
                except:
                    serial_number = 0
                resseq = int(line[22:26].split()[0])  # sequence identifier
                icode = line[26:27]  # insertion code
                if record_type == 'HETATM':  # hetero atom flag
                    if resname == "HOH" or resname == "WAT":
                        hetero_flag = "W"
                    else:
                        hetero_flag = "H"
                else:
                    hetero_flag = " "
                residue_id = (hetero_flag, resseq, icode)
                # atomic coordinates
                try:
                    x = float(line[30:38])
                    y = float(line[38:46])
                    z = float(line[46:54])
                except:
                    #Should we allow parsing to continue in permissive mode?
                    #If so what coordindates should we default to?  Easier to abort!
                    raise PDBConstructionException(\
                        "Invalid or missing coordinate(s) at line %i." \
                        % global_line_counter)
                coord = numpy.array((x, y, z), 'f')
                # occupancy & B factor
                try:
                    occupancy = float(line[54:60])
                except:
                    self._handle_PDB_exception("Invalid or missing occupancy",
                                               global_line_counter)
                    occupancy = 0.0  #Is one or zero a good default?
                try:
                    bfactor = float(line[60:66])
                except:
                    self._handle_PDB_exception("Invalid or missing B factor",
                                               global_line_counter)
                    bfactor = 0.0  #The PDB use a default of zero if the data is missing
                segid = line[72:76]
                element = line[76:78].strip()
                if current_segid != segid:
                    current_segid = segid
                    structure_builder.init_seg(current_segid)
                if current_chain_id != chainid:
                    current_chain_id = chainid
                    structure_builder.init_chain(current_chain_id)
                    current_residue_id = residue_id
                    current_resname = resname
                    try:
                        structure_builder.init_residue(resname, hetero_flag,
                                                       resseq, icode)
                    except PDBConstructionException, message:
                        self._handle_PDB_exception(message,
                                                   global_line_counter)
                elif current_residue_id != residue_id or current_resname != resname:
                    current_residue_id = residue_id
                    current_resname = resname
                    try:
                        structure_builder.init_residue(resname, hetero_flag,
                                                       resseq, icode)
                    except PDBConstructionException, message:
                        self._handle_PDB_exception(message,
                                                   global_line_counter)
                # init atom
                try:
                    structure_builder.init_atom(name, coord, bfactor,
                                                occupancy, altloc, fullname,
                                                serial_number, element)
                except PDBConstructionException, message:
                    self._handle_PDB_exception(message, global_line_counter)
Beispiel #9
0
class PDBP_read(object):
    def __init__(self,
                 get_header=False,
                 structure_builder=None,
                 PERMISSIVE=True):
        """arguments:
			PERMISSIVE, Evaluated as a Boolean. If ture, the exception are caught, 
			some residues or atoms will be missing.THESE EXCEPTIONS ARE DUE TO PROBLEMS IN THE PDB FILE!
			structure_builder, an optional user implemented StructureBuilder class.
			
			"""
        #get a structure_builder class
        if structure_builder is not None:
            self.structure_builder = structure_builder
        else:
            self.structure_builder = StructureBuilder()
        self.header = None
        self.trailer = None
        self.line_counter = 0
        self.PERMISSIVE = bool(PERMISSIVE)

    def get_structure(self, id, file):
        """return the structure.
		   argurements:
			-id - the name of the sturecture
			-file - pdb filename
		"""
        self.header = None
        self.trailer = None
        #make a StructureBuilder instance
        self.structure_builder.init_structure(id)
        if file[-2:] == 'gz':
            #try:
            #with open(file,'r+',encoding='utf-8') as handle:
            #self._parse(handle.readlines())
            with gzip.open(file, 'r') as handle:  #######按照行读取pdb文件
                self._parse(handle.read().decode("utf-8").split('\n'))
        else:
            try:
                with open(file, 'r') as handle:
                    self._parse(handle.readlines())
            except Exception:
                print("%s cannot be open!" % (file))
                #exit()
        #???????
        self.structure_builder.set_header(self.header)
        # return the structure instance
        structure = self.structure_builder.get_structure()
        return structure

    def get_header(self):
        """return the header"""
        return self.header

    def get_trailer(self):
        """return the trailer"""
        return self.trailer

    #private methods
    def _parse(self, header_coords_trailer):
        """parser the pdb file(private)"""
        self.coords_trailer = self._get_header(header_coords_trailer)
        ## parse the atomicdata; return the pdb file triler
        self.trailer = self._parse_coordinates(self.coords_trailer)

    def _get_header(self, header_coords_trailer):
        """get the header of the pdb file"""
        structure_builder = self.structure_builder
        i = 0
        line_nums = len(header_coords_trailer)
        '''
		for index, line in enumerate(header_coords_trailer):
			print(index, line)
		print(line_nums, type(header_coords_trailer))
		'''
        for i in range(0, line_nums):
            structure_builder.set_line_counter(i + 1)
            line = header_coords_trailer[i]
            record_type = line[0:6]
            if record_type == "ATOM  " or record_type == "HETATM" or record_type == "MODEL ":
                break
        #header = header_coords_trailer[0:i]
        #return the rest of the coodstrailer
        self.line_counter = i
        coords_trailer = header_coords_trailer[i:]
        #header_dict = self._parse_pdb_header_list(header)
        return coords_trailer

    def _parse_coordinates(self, coords_trailer):
        """parse the atomic data in teh PDB file """
        local_line_counter = 0
        #n=0
        structure_builder = self.structure_builder
        current_model_id = 0
        # Flag we have an open model
        model_open = 0
        current_chain_id = None
        current_segid = None
        current_residue_id = None
        current_resname = None
        lines_num1 = len(coords_trailer)
        for i in range(0, lines_num1):
            line = coords_trailer[i].rstrip('\n')
            record_type = line[0:6]
            global_line_counter = self.line_counter + local_line_counter + 1
            # the all lines nums include header coods and trailer
            structure_builder.set_line_counter(global_line_counter)
            if record_type == "ATOM  ":  #or record_type == "HETATM":
                #Initialize the Model - there was no explicit MODEL record
                if not model_open:
                    structure_builder.init_model(current_model_id)
                    current_model_id += 1
                    model_open = 1
                fullname = line[12:16]
                # get rid of whitespace in atom names
                split_list = fullname.split()
                if len(split_list) != 1:
                    # a atom has several species, eg "N B"
                    name = fullname
                else:
                    #eg: "CA"
                    name = split_list[0]
                altloc = line[16]
                resname = line[17:20]
                chainid = line[21]
                try:
                    serial_number = int(line[6:11])
                except Exception:
                    serial_number = 0
                resseq = int(line[22:26].split()[0])
                icode = line[26]
                if record_type == "HETATM":
                    if resname == "HOH" or resname == "WAT":
                        hetero_flag = "W"
                    else:
                        hetero_flag = "H"
                else:
                    hetero_flag = " "
                residue_id = (hetero_flag, resseq, icode)
                try:
                    x = float(line[30:38])
                    y = float(line[38:46])
                    z = float(line[46:54])
                except Exception:
                    raise PDBConstructionException(
                        "Invalid or missing coordinate(s) at line %i." %
                        global_line_counter)
                coord = numpy.array((x, y, z), "f")
                try:
                    occupancy = float(line[54:60])
                except Exception:
                    self._handle_PDB_exception("Invalid or missing occupancy",
                                               global_line_counter)
                    #occupancy = None  # Rather than arbitrary zero or one
                    occupancy = None  # Rather than arbitrary zero or one
                if occupancy is not None and occupancy < 0:
                    warnings.warn("Negative occupancy in one or more atoms",
                                  PDBConstructionWarning)
                try:
                    bfactor = float(line[60:66])
                except Exception:
                    self._handle_PDB_exception("Invalid or missing B factor",
                                               global_line_counter)
                    bfactor = 0.0  # The PDB use a default of zero if the data is missing
                segid = line[72:76]
                element = line[76:78].strip().upper()
                if current_segid != segid:
                    current_segid = segid
                    structure_builder.init_seg(current_segid)
                if current_chain_id != chainid:
                    current_chain_id = chainid
                    structure_builder.init_chain(current_chain_id)
                    current_residue_id = residue_id
                    current_resname = resname
                    try:
                        structure_builder.init_residue(resname, hetero_flag,
                                                       resseq, icode)
                    except PDBConstructionException as message:
                        self._handle_PDB_exception(message,
                                                   global_line_counter)
                elif current_residue_id != residue_id or current_resname != resname:
                    current_residue_id = residue_id
                    current_resname = resname
                    try:
                        structure_builder.init_residue(resname, hetero_flag,
                                                       resseq, icode)
                    except PDBConstructionException as message:
                        self._handle_PDB_exception(message,
                                                   global_line_counter)
                # init atom
                try:
                    structure_builder.init_atom(name, coord, bfactor,
                                                occupancy, altloc, fullname,
                                                serial_number, element)
                except PDBConstructionException as message:
                    self._handle_PDB_exception(message, global_line_counter)
            elif record_type == "MODEL ":
                try:
                    serial_num = int(line[10:14])
                except Exception:
                    self._handle_PDB_exception(
                        "Invalid or missing model serial number",
                        global_line_counter)
                    serial_num = 0
                structure_builder.init_model(current_model_id, serial_num)
                current_model_id += 1
                model_open = 1
                current_chain_id = None
                current_residue_id = None
            elif record_type == "END   " or record_type == "CONECT":
                # End of atomic data, return the trailer
                self.line_counter += local_line_counter
                return coords_trailer[local_line_counter:]
            elif record_type == "ENDMDL":
                model_open = 0
                current_chain_id = None
                current_residue_id = None
            elif record_type == "SIGUIJ":
                # standard deviation of anisotropic B factor
                siguij = [
                    float(x) for x in (line[28:35], line[35:42], line[42:49],
                                       line[49:56], line[56:63], line[63:70])
                ]
                # U sigma's are scaled by 10^4
                siguij_array = (numpy.array(siguij, "f") / 10000.0).astype("f")
                structure_builder.set_siguij(siguij_array)
            elif record_type == "SIGATM":
                # standard deviation of atomic positions
                sigatm = [
                    float(x) for x in (line[30:38], line[38:45], line[46:54],
                                       line[54:60], line[60:66])
                ]
                sigatm_array = numpy.array(sigatm, "f")
                structure_builder.set_sigatm(sigatm_array)
            local_line_counter += 1
        # EOF (does not end in END or CONECT)
        self.line_counter = self.line_counter + local_line_counter
        return []
        #info = (resname, resseq, serial_number,fullname, coord)
        #yield info

    def _handle_PDB_exception(self, message, line_counter):
        message = "%s at line %i." % (message, line_counter)
        if self.PERMISSIVE:
            # just print a warning - some residues/atoms may be missing
            warnings.warn(
                "PDBConstructionException: %s\n"
                "Exception ignored.\n"
                "Some atoms or residues may be missing in the data structure."
                % message, PDBConstructionWarning)
        else:
            # exceptions are fatal - raise again with new message (including line nr)
            raise PDBConstructionException(message)
Beispiel #10
0
class FastMMCIFParser(object):
    """Parse an MMCIF file and return a Structure object."""

    def __init__(self, structure_builder=None, QUIET=False):
        """Create a FastMMCIFParser object.

        The mmCIF parser calls a number of standard methods in an aggregated
        StructureBuilder object. Normally this object is instanciated by the
        parser object itself, but if the user provides his/her own
        StructureBuilder object, the latter is used instead.

        The main difference between this class and the regular MMCIFParser is
        that only 'ATOM' and 'HETATM' lines are parsed here. Use if you are
        interested only in coordinate information.

        Arguments:
         - structure_builder - an optional user implemented StructureBuilder class.
         - QUIET - Evaluated as a Boolean. If true, warnings issued in constructing
           the SMCRA data will be suppressed. If false (DEFAULT), they will be shown.
           These warnings might be indicative of problems in the mmCIF file!

        """
        if structure_builder is not None:
            self._structure_builder = structure_builder
        else:
            self._structure_builder = StructureBuilder()

        self.line_counter = 0
        self.build_structure = None
        self.QUIET = bool(QUIET)

    # Public methods

    def get_structure(self, structure_id, filename):
        """Return the structure.

        Arguments:
         - structure_id - string, the id that will be used for the structure
         - filename - name of the mmCIF file OR an open filehandle

        """
        with warnings.catch_warnings():
            if self.QUIET:
                warnings.filterwarnings("ignore", category=PDBConstructionWarning)
            with as_handle(filename) as handle:
                self._build_structure(structure_id, handle)

        return self._structure_builder.get_structure()

    # Private methods

    def _build_structure(self, structure_id, filehandle):

        # two special chars as placeholders in the mmCIF format
        # for item values that cannot be explicitly assigned
        # see: pdbx/mmcif syntax web page
        _unassigned = set(('.', '?'))

        # Read only _atom_site. and atom_site_anisotrop entries
        read_atom, read_aniso = False, False
        _fields, _records = [], []
        _anisof, _anisors = [], []
        for line in filehandle:
            if line.startswith('_atom_site.'):
                read_atom = True
                _fields.append(line.strip())
            elif line.startswith('_atom_site_anisotrop.'):
                read_aniso = True
                _anisof.append(line.strip())
            elif read_atom and line.startswith('#'):
                read_atom = False
            elif read_aniso and line.startswith('#'):
                read_aniso = False
            elif read_atom:
                _records.append(line.strip())
            elif read_aniso:
                _anisors.append(line.strip())

        # Dumping the shlex module here since this particular
        # category should be rather straightforward.
        # Quite a performance boost..
        _record_tbl = zip(*map(str.split, _records))
        _anisob_tbl = zip(*map(str.split, _anisors))

        mmcif_dict = dict(zip(_fields, _record_tbl))
        mmcif_dict.update(dict(zip(_anisof, _anisob_tbl)))

        # Build structure object
        atom_id_list = mmcif_dict["_atom_site.label_atom_id"]
        residue_id_list = mmcif_dict["_atom_site.label_comp_id"]

        try:
            element_list = mmcif_dict["_atom_site.type_symbol"]
        except KeyError:
            element_list = None

        seq_id_list = mmcif_dict["_atom_site.label_seq_id"]
        chain_id_list = mmcif_dict["_atom_site.auth_asym_id"]

        x_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_x"]]
        y_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_y"]]
        z_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_z"]]
        alt_list = mmcif_dict["_atom_site.label_alt_id"]
        icode_list = mmcif_dict["_atom_site.pdbx_PDB_ins_code"]
        b_factor_list = mmcif_dict["_atom_site.B_iso_or_equiv"]
        occupancy_list = mmcif_dict["_atom_site.occupancy"]
        fieldname_list = mmcif_dict["_atom_site.group_PDB"]

        try:
            serial_list = [int(n) for n in mmcif_dict["_atom_site.pdbx_PDB_model_num"]]
        except KeyError:
            # No model number column
            serial_list = None
        except ValueError:
            # Invalid model number (malformed file)
            raise PDBConstructionException("Invalid model number")

        try:
            aniso_u11 = mmcif_dict["_atom_site.aniso_U[1][1]"]
            aniso_u12 = mmcif_dict["_atom_site.aniso_U[1][2]"]
            aniso_u13 = mmcif_dict["_atom_site.aniso_U[1][3]"]
            aniso_u22 = mmcif_dict["_atom_site.aniso_U[2][2]"]
            aniso_u23 = mmcif_dict["_atom_site.aniso_U[2][3]"]
            aniso_u33 = mmcif_dict["_atom_site.aniso_U[3][3]"]
            aniso_flag = 1
        except KeyError:
            # no anisotropic B factors
            aniso_flag = 0

        # if auth_seq_id is present, we use this.
        # Otherwise label_seq_id is used.
        if "_atom_site.auth_seq_id" in mmcif_dict:
            seq_id_list = mmcif_dict["_atom_site.auth_seq_id"]
        else:
            seq_id_list = mmcif_dict["_atom_site.label_seq_id"]

        # Now loop over atoms and build the structure
        current_chain_id = None
        current_residue_id = None
        current_resname = None
        structure_builder = self._structure_builder
        structure_builder.init_structure(structure_id)
        structure_builder.init_seg(" ")

        # Historically, Biopython PDB parser uses model_id to mean array index
        # so serial_id means the Model ID specified in the file
        current_model_id = -1
        current_serial_id = -1
        for i in range(0, len(atom_id_list)):

            # set the line_counter for 'ATOM' lines only and not
            # as a global line counter found in the PDBParser()
            # this number should match the '_atom_site.id' index in the MMCIF
            structure_builder.set_line_counter(i)

            x = x_list[i]
            y = y_list[i]
            z = z_list[i]
            resname = residue_id_list[i]
            chainid = chain_id_list[i]
            altloc = alt_list[i]
            if altloc in _unassigned:
                altloc = " "
            int_resseq = int(seq_id_list[i])
            icode = icode_list[i]
            if icode in _unassigned:
                icode = " "
            name = atom_id_list[i].strip('"')  # Remove occasional " from quoted atom names (e.g. xNA)

            # occupancy & B factor
            try:
                tempfactor = float(b_factor_list[i])
            except ValueError:
                raise PDBConstructionException("Invalid or missing B factor")

            try:
                occupancy = float(occupancy_list[i])
            except ValueError:
                raise PDBConstructionException("Invalid or missing occupancy")

            fieldname = fieldname_list[i]
            if fieldname == "HETATM":
                hetatm_flag = "H"
            else:
                hetatm_flag = " "

            resseq = (hetatm_flag, int_resseq, icode)

            if serial_list is not None:
                # model column exists; use it
                serial_id = serial_list[i]
                if current_serial_id != serial_id:
                    # if serial changes, update it and start new model
                    current_serial_id = serial_id
                    current_model_id += 1
                    structure_builder.init_model(current_model_id, current_serial_id)
                    current_chain_id = None
                    current_residue_id = None
                    current_resname = None
            else:
                # no explicit model column; initialize single model
                structure_builder.init_model(current_model_id)

            if current_chain_id != chainid:
                current_chain_id = chainid
                structure_builder.init_chain(current_chain_id)
                current_residue_id = None
                current_resname = None

            if current_residue_id != resseq or current_resname != resname:
                current_residue_id = resseq
                current_resname = resname
                structure_builder.init_residue(resname, hetatm_flag, int_resseq, icode)

            coord = numpy.array((x, y, z), 'f')
            element = element_list[i] if element_list else None
            structure_builder.init_atom(name, coord, tempfactor, occupancy, altloc,
                name, element=element)
            if aniso_flag == 1:
                u = (aniso_u11[i], aniso_u12[i], aniso_u13[i],
                    aniso_u22[i], aniso_u23[i], aniso_u33[i])
                mapped_anisou = [float(x) for x in u]
                anisou_array = numpy.array(mapped_anisou, 'f')
                structure_builder.set_anisou(anisou_array)
Beispiel #11
0
def read_PIC(file: TextIO, verbose: bool = False) -> Structure:
    """Load Protein Internal Coordinate (.pic) data from file.

    PIC file format:
        - comment lines start with #
        - (optional) PDB HEADER record
           - idcode and deposition date recommended but optional
           - deposition date in PDB format or as changed by Biopython
        - (optional) PDB TITLE record
        - repeat:
           - Biopython Residue Full ID - sets residue IDs of returned structure
           - (optional) PDB N, CA, C ATOM records for chain start
           - (optional) PIC Hedra records for residue
           - (optional) PIC Dihedra records for residue
           - (optional) BFAC records listing AtomKeys and b-factors

    An improvement would define relative positions for HOH (water) entries.

    N.B. dihedron (i-1)C-N-CA-CB is ignored in assembly if O exists.

    C-beta is by default placed using O-C-CA-CB, but O is missing
    in some PDB file residues, which means the sidechain cannot be
    placed.  The alternate CB path (i-1)C-N-CA-CB is provided to
    circumvent this, but if this is needed then it must be adjusted in
    conjunction with PHI ((i-1)C-N-CA-C) as they overlap.  (i-1)C-N-CA-CB is
    included by default in .pic files for consistency and informational
    (e.g. statistics gathering) purposes, as otherwise the dihedron would only
    appear in the few cases it is needed for.

    :param Bio.File file: file name or handle
    :param bool verbose: complain when lines not as expected
    :returns: Biopython Structure object, Residues with .internal_coord attributes
        but no coordinates except for chain start N, CA, C atoms if supplied,
        **OR** None on parse fail (silent unless verbose=True)

    """
    pdb_hdr_re = re.compile(
        r"^HEADER\s{4}(?P<cf>.{1,40})"
        r"(?:\s+(?P<dd>\d\d\d\d-\d\d-\d\d|\d\d-\w\w\w-\d\d))?"
        r"(?:\s+(?P<id>[0-9A-Z]{4}))?\s*$")
    # ^\('(?P<pid>\w*)',\s(?P<mdl>\d+),\s'(?P<chn>\w)',\s\('(?P<het>\s|[\w-]+)',\s(?P<pos>\d+),\s'(?P<icode>\s|\w)'\)\)\s(?P<res>[A-Z]{3})\s(\[(?P<segid>[a-zA-z\s]{4})\])?\s*$
    pdb_ttl_re = re.compile(r"^TITLE\s{5}(?P<ttl>.+)\s*$")
    biop_id_re = re.compile(r"^\('(?P<pid>[^\s]*)',\s(?P<mdl>\d+),\s"
                            r"'(?P<chn>\s|\w)',\s\('(?P<het>\s|[\w\s-]+)"
                            r"',\s(?P<pos>-?\d+),\s'(?P<icode>\s|\w)'\)\)"
                            r"\s+(?P<res>[\w]{1,3})"
                            r"(\s\[(?P<segid>[a-zA-z\s]+)\])?"
                            r"\s*$")
    pdb_atm_re = re.compile(r"^ATOM\s\s(?:\s*(?P<ser>\d+))\s(?P<atm>[\w\s]{4})"
                            r"(?P<alc>\w|\s)(?P<res>[\w]{3})\s(?P<chn>.)"
                            r"(?P<pos>[\s\-\d]{4})(?P<icode>[A-Za-z\s])\s\s\s"
                            r"(?P<x>[\s\-\d\.]{8})(?P<y>[\s\-\d\.]{8})"
                            r"(?P<z>[\s\-\d\.]{8})(?P<occ>[\s\d\.]{6})"
                            r"(?P<tfac>[\s\d\.]{6})\s{6}"
                            r"(?P<segid>[a-zA-z\s]{4})(?P<elm>.{2})"
                            r"(?P<chg>.{2})?\s*$")
    bfac_re = re.compile(r"^BFAC:\s([^\s]+\s+[\-\d\.]+)"
                         r"\s*([^\s]+\s+[\-\d\.]+)?"
                         r"\s*([^\s]+\s+[\-\d\.]+)?"
                         r"\s*([^\s]+\s+[\-\d\.]+)?"
                         r"\s*([^\s]+\s+[\-\d\.]+)?")
    bfac2_re = re.compile(r"([^\s]+)\s+([\-\d\.]+)")
    struct_builder = StructureBuilder()

    # init empty header dict
    # - could use to parse HEADER and TITLE lines except
    #   deposition_date format changed from original PDB header
    header_dict = _parse_pdb_header_list([])

    curr_SMCS = [None, None, None, None]  # struct model chain seg
    SMCS_init = [
        struct_builder.init_structure,
        struct_builder.init_model,
        struct_builder.init_chain,
        struct_builder.init_seg,
    ]

    sb_res = None

    with as_handle(file, mode="r") as handle:
        for aline in handle.readlines():
            if aline.startswith("#"):
                pass  # skip comment lines
            elif aline.startswith("HEADER "):
                m = pdb_hdr_re.match(aline)
                if m:
                    header_dict["head"] = m.group("cf")  # classification
                    header_dict["idcode"] = m.group("id")
                    header_dict["deposition_date"] = m.group("dd")
                elif verbose:
                    print("Reading pic file", file, "HEADER parse fail: ",
                          aline)
            elif aline.startswith("TITLE "):
                m = pdb_ttl_re.match(aline)
                if m:
                    header_dict["name"] = m.group("ttl").strip()
                    # print('TTL: ', m.group('ttl').strip())
                elif verbose:
                    print("Reading pic file", file, "TITLE parse fail:, ",
                          aline)
            elif aline.startswith("("):  # Biopython ID line for Residue
                m = biop_id_re.match(aline)
                if m:
                    # check SMCS = Structure, Model, Chain, SegID
                    segid = m.group(9)
                    if segid is None:
                        segid = "    "
                    this_SMCS = [
                        m.group(1),
                        int(m.group(2)),
                        m.group(3), segid
                    ]
                    if curr_SMCS != this_SMCS:
                        # init new SMCS level as needed
                        for i in range(4):
                            if curr_SMCS[i] != this_SMCS[i]:
                                SMCS_init[i](this_SMCS[i])
                                curr_SMCS[i] = this_SMCS[i]
                                if 0 == i:
                                    # 0 = init structure so add header
                                    struct_builder.set_header(header_dict)
                                elif 1 == i:
                                    # new model means new chain and new segid
                                    curr_SMCS[2] = curr_SMCS[3] = None

                    struct_builder.init_residue(
                        m.group("res"),
                        m.group("het"),
                        int(m.group("pos")),
                        m.group("icode"),
                    )

                    sb_res = struct_builder.residue
                    if 2 == sb_res.is_disordered():
                        for r in sb_res.child_dict.values():
                            if not r.internal_coord:
                                sb_res = r
                                break
                    sb_res.internal_coord = IC_Residue(sb_res)
                    # print('res id:', m.groupdict())
                    # print(report_IC(struct_builder.get_structure()))
                else:
                    if verbose:
                        print("Reading pic file", file,
                              "residue ID parse fail: ", aline)
                    return None
            elif aline.startswith("ATOM "):
                m = pdb_atm_re.match(aline)
                if m:
                    if sb_res is None:
                        # ATOM without res spec already loaded, not a pic file
                        if verbose:
                            print(
                                "Reading pic file",
                                file,
                                "ATOM without residue configured:, ",
                                aline,
                            )
                        return None
                    if sb_res.resname != m.group("res") or sb_res.id[1] != int(
                            m.group("pos")):
                        if verbose:
                            print(
                                "Reading pic file",
                                file,
                                "ATOM not in configured residue (",
                                sb_res.resname,
                                str(sb_res.id),
                                "):",
                                aline,
                            )
                        return None
                    coord = numpy.array(
                        (float(m.group("x")), float(
                            m.group("y")), float(m.group("z"))),
                        "f",
                    )
                    struct_builder.init_atom(
                        m.group("atm").strip(),
                        coord,
                        float(m.group("tfac")),
                        float(m.group("occ")),
                        m.group("alc"),
                        m.group("atm"),
                        int(m.group("ser")),
                        m.group("elm").strip(),
                    )

                    # print('atom: ', m.groupdict())
                # elif verbose:
                #     print("Reading pic file", file, "ATOM parse fail:", aline)
            elif aline.startswith("BFAC: "):
                m = bfac_re.match(aline)
                if m:
                    for bfac_pair in m.groups():
                        if bfac_pair is not None:
                            m2 = bfac2_re.match(bfac_pair)
                            if m2 and sb_res is not None and sb_res.internal_coord:
                                rp = sb_res.internal_coord
                                rp.bfactors[m2.group(1)] = float(m2.group(2))
                # else:
                #    print('Reading pic file', file, 'B-factor line fail: ', aline)
            else:
                m = Edron.edron_re.match(aline)
                if m and sb_res is not None:
                    sb_res.internal_coord.load_PIC(m.groupdict())
                elif m:
                    print(
                        "PIC file: ",
                        file,
                        " error: no residue info before reading (di/h)edron data: ",
                        aline,
                    )
                    return None
                elif aline.strip():
                    if verbose:
                        print("Reading PIC file", file, "parse fail on: .",
                              aline, ".")
                    return None

    struct = struct_builder.get_structure()
    for chn in struct.get_chains():
        chnp = chn.internal_coord = IC_Chain(chn)
        # done in IC_Chain init : chnp.set_residues()
        chnp.link_residues()
        chnp.init_edra()

    # print(report_PIC(struct_builder.get_structure()))
    return struct
Beispiel #12
0
class MMCIFParser(object):
    def get_structure(self, structure_id, filename):
        self._mmcif_dict = MMCIF2Dict(filename)
        self._structure_builder = StructureBuilder()
        self._build_structure(structure_id)
        return self._structure_builder.get_structure()

    def _build_structure(self, structure_id):
        mmcif_dict = self._mmcif_dict
        atom_id_list = mmcif_dict["_atom_site.label_atom_id"]
        residue_id_list = mmcif_dict["_atom_site.label_comp_id"]
        try:
            element_list = mmcif_dict["_atom_site.type_symbol"]
        except KeyError:
            element_list = None
        seq_id_list = mmcif_dict["_atom_site.label_seq_id"]
        chain_id_list = mmcif_dict["_atom_site.label_asym_id"]
        x_list = map(float, mmcif_dict["_atom_site.Cartn_x"])
        y_list = map(float, mmcif_dict["_atom_site.Cartn_y"])
        z_list = map(float, mmcif_dict["_atom_site.Cartn_z"])
        alt_list = mmcif_dict["_atom_site.label_alt_id"]
        b_factor_list = mmcif_dict["_atom_site.B_iso_or_equiv"]
        occupancy_list = mmcif_dict["_atom_site.occupancy"]
        fieldname_list = mmcif_dict["_atom_site.group_PDB"]
        try:
            serial_list = [
                int(n) for n in mmcif_dict["_atom_site.pdbx_PDB_model_num"]
            ]
        except KeyError:
            # No model number column
            serial_list = None
        except ValueError:
            # Invalid model number (malformed file)
            raise PDBConstructionException("Invalid model number")
        try:
            aniso_u11 = mmcif_dict["_atom_site.aniso_U[1][1]"]
            aniso_u12 = mmcif_dict["_atom_site.aniso_U[1][2]"]
            aniso_u13 = mmcif_dict["_atom_site.aniso_U[1][3]"]
            aniso_u22 = mmcif_dict["_atom_site.aniso_U[2][2]"]
            aniso_u23 = mmcif_dict["_atom_site.aniso_U[2][3]"]
            aniso_u33 = mmcif_dict["_atom_site.aniso_U[3][3]"]
            aniso_flag = 1
        except KeyError:
            # no anisotropic B factors
            aniso_flag = 0
        # if auth_seq_id is present, we use this.
        # Otherwise label_seq_id is used.
        if "_atom_site.auth_seq_id" in mmcif_dict:
            seq_id_list = mmcif_dict["_atom_site.auth_seq_id"]
        else:
            seq_id_list = mmcif_dict["_atom_site.label_seq_id"]
        # Now loop over atoms and build the structure
        current_chain_id = None
        current_residue_id = None
        structure_builder = self._structure_builder
        structure_builder.init_structure(structure_id)
        structure_builder.init_seg(" ")
        # Historically, Biopython PDB parser uses model_id to mean array index
        # so serial_id means the Model ID specified in the file
        current_model_id = 0
        current_serial_id = 0
        for i in xrange(0, len(atom_id_list)):
            x = x_list[i]
            y = y_list[i]
            z = z_list[i]
            resname = residue_id_list[i]
            chainid = chain_id_list[i]
            altloc = alt_list[i]
            if altloc == ".":
                altloc = " "
            resseq = seq_id_list[i]
            name = atom_id_list[i]
            tempfactor = b_factor_list[i]
            occupancy = occupancy_list[i]
            fieldname = fieldname_list[i]
            if fieldname == "HETATM":
                hetatm_flag = "H"
            else:
                hetatm_flag = " "
            if serial_list is not None:
                # model column exists; use it
                serial_id = serial_list[i]
                if current_serial_id != serial_id:
                    # if serial changes, update it and start new model
                    current_serial_id = serial_id
                    structure_builder.init_model(current_model_id,
                                                 current_serial_id)
                    current_model_id += 1
            else:
                # no explicit model column; initialize single model
                structure_builder.init_model(current_model_id)
            if current_chain_id != chainid:
                current_chain_id = chainid
                structure_builder.init_chain(current_chain_id)
                current_residue_id = resseq
                icode, int_resseq = self._get_icode(resseq)
                structure_builder.init_residue(resname, hetatm_flag,
                                               int_resseq, icode)
            elif current_residue_id != resseq:
                current_residue_id = resseq
                icode, int_resseq = self._get_icode(resseq)
                structure_builder.init_residue(resname, hetatm_flag,
                                               int_resseq, icode)
            coord = numpy.array((x, y, z), 'f')
            element = element_list[i] if element_list else None
            structure_builder.init_atom(name,
                                        coord,
                                        tempfactor,
                                        occupancy,
                                        altloc,
                                        name,
                                        element=element)
            if aniso_flag == 1:
                u = (aniso_u11[i], aniso_u12[i], aniso_u13[i], aniso_u22[i],
                     aniso_u23[i], aniso_u33[i])
                mapped_anisou = map(float, u)
                anisou_array = numpy.array(mapped_anisou, 'f')
                structure_builder.set_anisou(anisou_array)
        # Now try to set the cell
        try:
            a = float(mmcif_dict["_cell.length_a"])
            b = float(mmcif_dict["_cell.length_b"])
            c = float(mmcif_dict["_cell.length_c"])
            alpha = float(mmcif_dict["_cell.angle_alpha"])
            beta = float(mmcif_dict["_cell.angle_beta"])
            gamma = float(mmcif_dict["_cell.angle_gamma"])
            cell = numpy.array((a, b, c, alpha, beta, gamma), 'f')
            spacegroup = mmcif_dict["_symmetry.space_group_name_H-M"]
            spacegroup = spacegroup[1:-1]  # get rid of quotes!!
            if spacegroup == None:
                raise Exception
            structure_builder.set_symmetry(spacegroup, cell)
        except:
            pass  # no cell found, so just ignore

    def _get_icode(self, resseq):
        """Tries to return the icode. In MMCIF files this is just part of
        resseq! In PDB files, it's a separate field."""
        last_resseq_char = resseq[-1]
        if last_resseq_char in ascii_letters:
            icode = last_resseq_char
            int_resseq = int(resseq[0:-1])
        else:
            icode = " "
            int_resseq = int(resseq)
        return icode, int_resseq
Beispiel #13
0
class PDBParser(object):
    """
    Parse a PDB file and return a Structure object.
    """

    def __init__(self, PERMISSIVE=True, get_header=False,
                 structure_builder=None, QUIET=False):
        """
        The PDB parser call a number of standard methods in an aggregated
        StructureBuilder object. Normally this object is instanciated by the
        PDBParser object itself, but if the user provides his/her own
        StructureBuilder object, the latter is used instead.

        Arguments:

        o PERMISSIVE - Evaluated as a Boolean. If false, exceptions in
        constructing the SMCRA data structure are fatal. If true (DEFAULT),
        the exceptions are caught, but some residues or atoms will be missing.
        THESE EXCEPTIONS ARE DUE TO PROBLEMS IN THE PDB FILE!.

        o structure_builder - an optional user implemented StructureBuilder class.

        o QUIET - Evaluated as a Boolean. If true, warnings issued in constructing
        the SMCRA data will be suppressed. If false (DEFAULT), they will be shown.
        These warnings might be indicative of problems in the PDB file!
        """
        if structure_builder is not None:
            self.structure_builder = structure_builder
        else:
            self.structure_builder = StructureBuilder()
        self.header = None
        self.trailer = None
        self.line_counter = 0
        self.PERMISSIVE = bool(PERMISSIVE)
        self.QUIET = bool(QUIET)

    # Public methods

    def get_structure(self, id, file):
        """Return the structure.

        Arguments:
        o id - string, the id that will be used for the structure
        o file - name of the PDB file OR an open filehandle
        """

        if self.QUIET:
            warning_list = warnings.filters[:]
            warnings.filterwarnings("ignore", category=PDBConstructionWarning)

        self.header = None
        self.trailer = None
        # Make a StructureBuilder instance (pass id of structure as parameter)
        self.structure_builder.init_structure(id)

        with as_handle(file) as handle:
            self._parse(handle.readlines())

        self.structure_builder.set_header(self.header)
        # Return the Structure instance
        structure = self.structure_builder.get_structure()

        if self.QUIET:
            warnings.filters = warning_list

        return structure

    def get_header(self):
        "Return the header."
        return self.header

    def get_trailer(self):
        "Return the trailer."
        return self.trailer

    # Private methods

    def _parse(self, header_coords_trailer):
        "Parse the PDB file."
        # Extract the header; return the rest of the file
        self.header, coords_trailer = self._get_header(header_coords_trailer)
        # Parse the atomic data; return the PDB file trailer
        self.trailer = self._parse_coordinates(coords_trailer)

    def _get_header(self, header_coords_trailer):
        "Get the header of the PDB file, return the rest."
        structure_builder = self.structure_builder
        i = 0
        for i in range(0, len(header_coords_trailer)):
            structure_builder.set_line_counter(i + 1)
            line = header_coords_trailer[i]
            record_type = line[0:6]
            if record_type == "ATOM  " or record_type == "HETATM" or record_type == "MODEL ":
                break
        header = header_coords_trailer[0:i]
        # Return the rest of the coords+trailer for further processing
        self.line_counter = i
        coords_trailer = header_coords_trailer[i:]
        header_dict = _parse_pdb_header_list(header)
        return header_dict, coords_trailer

    def _parse_coordinates(self, coords_trailer):
        "Parse the atomic data in the PDB file."
        local_line_counter = 0
        structure_builder = self.structure_builder
        current_model_id = 0
        # Flag we have an open model
        model_open = 0
        current_chain_id = None
        current_segid = None
        current_residue_id = None
        current_resname = None
        for i in range(0, len(coords_trailer)):
            line = coords_trailer[i]
            record_type = line[0:6]
            global_line_counter = self.line_counter + local_line_counter + 1
            structure_builder.set_line_counter(global_line_counter)
            if record_type == "ATOM  " or record_type == "HETATM":
                # Initialize the Model - there was no explicit MODEL record
                if not model_open:
                    structure_builder.init_model(current_model_id)
                    current_model_id += 1
                    model_open = 1
                fullname = line[12:16]
                # get rid of whitespace in atom names
                split_list = fullname.split()
                if len(split_list) != 1:
                    # atom name has internal spaces, e.g. " N B ", so
                    # we do not strip spaces
                    name = fullname
                else:
                    # atom name is like " CA ", so we can strip spaces
                    name = split_list[0]
                altloc = line[16]
                resname = line[17:20]
                chainid = line[21]
                try:
                    serial_number = int(line[6:11])
                except:
                    serial_number = 0
                resseq = int(line[22:26].split()[0])  # sequence identifier
                icode = line[26]  # insertion code
                if record_type == "HETATM":  # hetero atom flag
                    if resname == "HOH" or resname == "WAT":
                        hetero_flag = "W"
                    else:
                        hetero_flag = "H"
                else:
                    hetero_flag = " "
                residue_id = (hetero_flag, resseq, icode)
                # atomic coordinates
                try:
                    x = float(line[30:38])
                    y = float(line[38:46])
                    z = float(line[46:54])
                except:
                    # Should we allow parsing to continue in permissive mode?
                    # If so, what coordinates should we default to?  Easier to abort!
                    raise PDBConstructionException("Invalid or missing coordinate(s) at line %i."
                                                   % global_line_counter)
                coord = numpy.array((x, y, z), "f")
                # occupancy & B factor
                try:
                    occupancy = float(line[54:60])
                except:
                    self._handle_PDB_exception("Invalid or missing occupancy",
                                               global_line_counter)
                    occupancy = None # Rather than arbitrary zero or one
                try:
                    bfactor = float(line[60:66])
                except:
                    self._handle_PDB_exception("Invalid or missing B factor",
                                               global_line_counter)
                    bfactor = 0.0  # The PDB use a default of zero if the data is missing
                segid = line[72:76]
                element = line[76:78].strip()
                if current_segid != segid:
                    current_segid = segid
                    structure_builder.init_seg(current_segid)
                if current_chain_id != chainid:
                    current_chain_id = chainid
                    structure_builder.init_chain(current_chain_id)
                    current_residue_id = residue_id
                    current_resname = resname
                    try:
                        structure_builder.init_residue(resname, hetero_flag, resseq, icode)
                    except PDBConstructionException, message:
                        self._handle_PDB_exception(message, global_line_counter)
                elif current_residue_id != residue_id or current_resname != resname:
                    current_residue_id = residue_id
                    current_resname = resname
                    try:
                        structure_builder.init_residue(resname, hetero_flag, resseq, icode)
                    except PDBConstructionException, message:
                        self._handle_PDB_exception(message, global_line_counter)
                # init atom
                try:
                    structure_builder.init_atom(name, coord, bfactor, occupancy, altloc,
                                                fullname, serial_number, element)
                except PDBConstructionException, message:
                    self._handle_PDB_exception(message, global_line_counter)
class PDBParser(object):
    """Parse a PDB file and return a Structure object."""
    def __init__(self,
                 PERMISSIVE=True,
                 get_header=False,
                 structure_builder=None,
                 QUIET=False):
        """Create a PDBParser object.

        The PDB parser call a number of standard methods in an aggregated
        StructureBuilder object. Normally this object is instanciated by the
        PDBParser object itself, but if the user provides his/her own
        StructureBuilder object, the latter is used instead.

        Arguments:
         - PERMISSIVE - Evaluated as a Boolean. If false, exceptions in
           constructing the SMCRA data structure are fatal. If true (DEFAULT),
           the exceptions are caught, but some residues or atoms will be missing.
           THESE EXCEPTIONS ARE DUE TO PROBLEMS IN THE PDB FILE!.
         - get_header - unused argument kept for historical compatibilty.
         - structure_builder - an optional user implemented StructureBuilder class.
         - QUIET - Evaluated as a Boolean. If true, warnings issued in constructing
           the SMCRA data will be suppressed. If false (DEFAULT), they will be shown.
           These warnings might be indicative of problems in the PDB file!

        """
        # get_header is not used but is left in for API compatibility
        if structure_builder is not None:
            self.structure_builder = structure_builder
        else:
            self.structure_builder = StructureBuilder()
        self.header = None
        self.trailer = None
        self.line_counter = 0
        self.PERMISSIVE = bool(PERMISSIVE)
        self.QUIET = bool(QUIET)

    # Public methods

    def get_structure(self, id, file):
        """Return the structure.

        Arguments:
         - id - string, the id that will be used for the structure
         - file - name of the PDB file OR an open filehandle

        """
        with warnings.catch_warnings():
            if self.QUIET:
                warnings.filterwarnings("ignore",
                                        category=PDBConstructionWarning)

            self.header = None
            self.trailer = None
            # Make a StructureBuilder instance (pass id of structure as parameter)
            self.structure_builder.init_structure(id)

            with as_handle(file, mode="rU") as handle:
                lines = handle.readlines()
                if not lines:
                    raise ValueError("Empty file.")
                self._parse(lines)

            self.structure_builder.set_header(self.header)
            # Return the Structure instance
            structure = self.structure_builder.get_structure()

        return structure

    def get_header(self):
        """Return the header."""
        return self.header

    def get_trailer(self):
        """Return the trailer."""
        return self.trailer

    # Private methods

    def _parse(self, header_coords_trailer):
        """Parse the PDB file (PRIVATE)."""
        # Extract the header; return the rest of the file
        self.header, coords_trailer = self._get_header(header_coords_trailer)
        # Parse the atomic data; return the PDB file trailer
        self.trailer = self._parse_coordinates(coords_trailer)

    def _get_header(self, header_coords_trailer):
        """Get the header of the PDB file, return the rest (PRIVATE)."""
        structure_builder = self.structure_builder
        i = 0
        for i in range(0, len(header_coords_trailer)):
            structure_builder.set_line_counter(i + 1)
            line = header_coords_trailer[i]
            record_type = line[0:6]
            if record_type in ("ATOM  ", "HETATM", "MODEL "):
                break
        header = header_coords_trailer[0:i]
        # Return the rest of the coords+trailer for further processing
        self.line_counter = i
        coords_trailer = header_coords_trailer[i:]
        header_dict = _parse_pdb_header_list(header)
        return header_dict, coords_trailer

    def _parse_coordinates(self, coords_trailer):
        """Parse the atomic data in the PDB file (PRIVATE)."""
        local_line_counter = 0
        structure_builder = self.structure_builder
        current_model_id = 0
        # Flag we have an open model
        model_open = 0
        current_chain_id = None
        current_segid = None
        current_residue_id = None
        current_resname = None
        for i in range(0, len(coords_trailer)):
            line = coords_trailer[i].rstrip("\n")
            record_type = line[0:6]
            global_line_counter = self.line_counter + local_line_counter + 1
            structure_builder.set_line_counter(global_line_counter)
            if record_type == "ATOM  " or record_type == "HETATM":
                # Initialize the Model - there was no explicit MODEL record
                if not model_open:
                    structure_builder.init_model(current_model_id)
                    current_model_id += 1
                    model_open = 1
                fullname = line[12:16]
                # get rid of whitespace in atom names
                split_list = fullname.split()
                if len(split_list) != 1:
                    # atom name has internal spaces, e.g. " N B ", so
                    # we do not strip spaces
                    name = fullname
                else:
                    # atom name is like " CA ", so we can strip spaces
                    name = split_list[0]
                altloc = line[16]
                resname = line[17:20]
                chainid = line[21]
                try:
                    serial_number = int(line[6:11])
                except Exception:
                    serial_number = 0
                resseq = int(line[22:26].split()[0])  # sequence identifier
                icode = line[26]  # insertion code
                if record_type == "HETATM":  # hetero atom flag
                    if resname == "HOH" or resname == "WAT":
                        hetero_flag = "W"
                    else:
                        hetero_flag = "H"
                else:
                    hetero_flag = " "
                residue_id = (hetero_flag, resseq, icode)
                # atomic coordinates
                try:
                    x = float(line[30:38])
                    y = float(line[38:46])
                    z = float(line[46:54])
                except Exception:
                    # Should we allow parsing to continue in permissive mode?
                    # If so, what coordinates should we default to?  Easier to abort!
                    raise PDBConstructionException(
                        "Invalid or missing coordinate(s) at line %i." %
                        global_line_counter)
                coord = numpy.array((x, y, z), "f")
                # occupancy & B factor
                try:
                    occupancy = float(line[54:60])
                except Exception:
                    self._handle_PDB_exception("Invalid or missing occupancy",
                                               global_line_counter)
                    occupancy = None  # Rather than arbitrary zero or one
                if occupancy is not None and occupancy < 0:
                    # TODO - Should this be an error in strict mode?
                    # self._handle_PDB_exception("Negative occupancy",
                    #                            global_line_counter)
                    # This uses fixed text so the warning occurs once only:
                    warnings.warn(
                        "Negative occupancy in one or more atoms",
                        PDBConstructionWarning,
                    )
                try:
                    bfactor = float(line[60:66])
                except Exception:
                    self._handle_PDB_exception("Invalid or missing B factor",
                                               global_line_counter)
                    bfactor = 0.0  # PDB uses a default of zero if missing
                segid = line[72:76]
                element = line[76:78].strip().upper()
                if current_segid != segid:
                    current_segid = segid
                    structure_builder.init_seg(current_segid)
                if current_chain_id != chainid:
                    current_chain_id = chainid
                    structure_builder.init_chain(current_chain_id)
                    current_residue_id = residue_id
                    current_resname = resname
                    try:
                        structure_builder.init_residue(resname, hetero_flag,
                                                       resseq, icode)
                    except PDBConstructionException as message:
                        self._handle_PDB_exception(message,
                                                   global_line_counter)
                elif current_residue_id != residue_id or current_resname != resname:
                    current_residue_id = residue_id
                    current_resname = resname
                    try:
                        structure_builder.init_residue(resname, hetero_flag,
                                                       resseq, icode)
                    except PDBConstructionException as message:
                        self._handle_PDB_exception(message,
                                                   global_line_counter)
                # init atom
                try:
                    structure_builder.init_atom(
                        name,
                        coord,
                        bfactor,
                        occupancy,
                        altloc,
                        fullname,
                        serial_number,
                        element,
                    )
                except PDBConstructionException as message:
                    self._handle_PDB_exception(message, global_line_counter)
            elif record_type == "ANISOU":
                anisou = [
                    float(x) for x in (
                        line[28:35],
                        line[35:42],
                        line[43:49],
                        line[49:56],
                        line[56:63],
                        line[63:70],
                    )
                ]
                # U's are scaled by 10^4
                anisou_array = (numpy.array(anisou, "f") / 10000.0).astype("f")
                structure_builder.set_anisou(anisou_array)
            elif record_type == "MODEL ":
                try:
                    serial_num = int(line[10:14])
                except Exception:
                    self._handle_PDB_exception(
                        "Invalid or missing model serial number",
                        global_line_counter)
                    serial_num = 0
                structure_builder.init_model(current_model_id, serial_num)
                current_model_id += 1
                model_open = 1
                current_chain_id = None
                current_residue_id = None
            elif record_type == "END   " or record_type == "CONECT":
                # End of atomic data, return the trailer
                self.line_counter += local_line_counter
                return coords_trailer[local_line_counter:]
            elif record_type == "ENDMDL":
                model_open = 0
                current_chain_id = None
                current_residue_id = None
            elif record_type == "SIGUIJ":
                # standard deviation of anisotropic B factor
                siguij = [
                    float(x) for x in (
                        line[28:35],
                        line[35:42],
                        line[42:49],
                        line[49:56],
                        line[56:63],
                        line[63:70],
                    )
                ]
                # U sigma's are scaled by 10^4
                siguij_array = (numpy.array(siguij, "f") / 10000.0).astype("f")
                structure_builder.set_siguij(siguij_array)
            elif record_type == "SIGATM":
                # standard deviation of atomic positions
                sigatm = [
                    float(x) for x in (
                        line[30:38],
                        line[38:45],
                        line[46:54],
                        line[54:60],
                        line[60:66],
                    )
                ]
                sigatm_array = numpy.array(sigatm, "f")
                structure_builder.set_sigatm(sigatm_array)
            local_line_counter += 1
        # EOF (does not end in END or CONECT)
        self.line_counter = self.line_counter + local_line_counter
        return []

    def _handle_PDB_exception(self, message, line_counter):
        """Handle exception (PRIVATE).

        This method catches an exception that occurs in the StructureBuilder
        object (if PERMISSIVE), or raises it again, this time adding the
        PDB line number to the error message.
        """
        message = "%s at line %i." % (message, line_counter)
        if self.PERMISSIVE:
            # just print a warning - some residues/atoms may be missing
            warnings.warn(
                "PDBConstructionException: %s\n"
                "Exception ignored.\n"
                "Some atoms or residues may be missing in the data structure."
                % message,
                PDBConstructionWarning,
            )
        else:
            # exceptions are fatal - raise again with new message (including line nr)
            raise PDBConstructionException(message)
            # Convert SS to bfactor code
            sscode = ss_to_code[sstype]
            # Coarse grain residue
            residue_restraints, beads = MARTINI(residue)
            structure_builder.init_residue(residue.resname, residue.id[0],
                                           residue.id[1], residue.id[2])
            # Populate residue
            for name, coord in beads:
                structure_builder.init_atom(name, coord, sscode, 1.00, " ",
                                            name, nbeads, "C")
                nbeads += 1
            # Save restraints
            tbl_cg_to_aa.extend(residue_restraints)

cg_model = structure_builder.get_structure()

# output sequence and ss information
print("%s:\t" % chain.id, aa_seq)
print("%s:\t" % chain.id, martini_types)
print("%s:\t" % chain.id,
      ''.join(map(lambda x: str(ss_to_code[x]), martini_types)))

# Write coarse grained structure
io.set_structure(cg_model)
io.save('%s_cg.pdb' % (pdbf_path[:-4]), write_end=1)
# Write Restraints
tbl_file = open('%s_cg_to_aa.tbl' % pdbf_path[:-4], 'w')
tbl_file.write('\n'.join(tbl_cg_to_aa))
tbl_file.close()
Beispiel #16
0
class MMCIFParser(object):
    def get_structure(self, structure_id, filename):
        self._mmcif_dict=MMCIF2Dict(filename)
        self._structure_builder=StructureBuilder()
        self._build_structure(structure_id)
        return self._structure_builder.get_structure()

    def _build_structure(self, structure_id):
        mmcif_dict=self._mmcif_dict
        atom_id_list=mmcif_dict["_atom_site.label_atom_id"]
        residue_id_list=mmcif_dict["_atom_site.label_comp_id"]
        try:
            element_list = mmcif_dict["_atom_site.type_symbol"]
        except KeyError:
            element_list = None
        seq_id_list=mmcif_dict["_atom_site.label_seq_id"]
        chain_id_list=mmcif_dict["_atom_site.label_asym_id"]
        x_list=map(float, mmcif_dict["_atom_site.Cartn_x"])
        y_list=map(float, mmcif_dict["_atom_site.Cartn_y"])
        z_list=map(float, mmcif_dict["_atom_site.Cartn_z"])
        alt_list=mmcif_dict["_atom_site.label_alt_id"]
        b_factor_list=mmcif_dict["_atom_site.B_iso_or_equiv"]
        occupancy_list=mmcif_dict["_atom_site.occupancy"]
        fieldname_list=mmcif_dict["_atom_site.group_PDB"]
        try:
            serial_list = [int(n) for n in mmcif_dict["_atom_site.pdbx_PDB_model_num"]]
        except KeyError:
            # No model number column
            serial_list = None
        except ValueError:
            # Invalid model number (malformed file)
            raise PDBConstructionException("Invalid model number")
        try:
            aniso_u11=mmcif_dict["_atom_site.aniso_U[1][1]"]
            aniso_u12=mmcif_dict["_atom_site.aniso_U[1][2]"]
            aniso_u13=mmcif_dict["_atom_site.aniso_U[1][3]"]
            aniso_u22=mmcif_dict["_atom_site.aniso_U[2][2]"]
            aniso_u23=mmcif_dict["_atom_site.aniso_U[2][3]"]
            aniso_u33=mmcif_dict["_atom_site.aniso_U[3][3]"]
            aniso_flag=1
        except KeyError:
            # no anisotropic B factors
            aniso_flag=0
        # if auth_seq_id is present, we use this.
        # Otherwise label_seq_id is used.
        if "_atom_site.auth_seq_id" in mmcif_dict:
            seq_id_list=mmcif_dict["_atom_site.auth_seq_id"]
        else:
            seq_id_list=mmcif_dict["_atom_site.label_seq_id"]
        # Now loop over atoms and build the structure
        current_chain_id=None
        current_residue_id=None
        structure_builder=self._structure_builder
        structure_builder.init_structure(structure_id)
        structure_builder.init_seg(" ")
        # Historically, Biopython PDB parser uses model_id to mean array index
        # so serial_id means the Model ID specified in the file
        current_model_id = 0
        current_serial_id = 0
        for i in xrange(0, len(atom_id_list)):
            x=x_list[i]
            y=y_list[i]
            z=z_list[i]
            resname=residue_id_list[i]
            chainid=chain_id_list[i]
            altloc=alt_list[i]
            if altloc==".":
                altloc=" "
            resseq=seq_id_list[i]
            name=atom_id_list[i]
            # occupancy & B factor
            try:
                tempfactor=float(b_factor_list[i])
            except ValueError:
                raise PDBConstructionException("Invalid or missing B factor")
            try:
                occupancy=float(occupancy_list[i])
            except ValueError:
                raise PDBConstructionException("Invalid or missing occupancy")
            fieldname=fieldname_list[i]
            if fieldname=="HETATM":
                hetatm_flag="H"
            else:
                hetatm_flag=" "
            if serial_list is not None:
                # model column exists; use it
                serial_id = serial_list[i]
                if current_serial_id != serial_id:
                    # if serial changes, update it and start new model
                    current_serial_id = serial_id
                    structure_builder.init_model(current_model_id, current_serial_id)
                    current_model_id += 1
            else:
                # no explicit model column; initialize single model
                structure_builder.init_model(current_model_id)
            if current_chain_id!=chainid:
                current_chain_id=chainid
                structure_builder.init_chain(current_chain_id)
                current_residue_id=resseq
                icode, int_resseq=self._get_icode(resseq)
                structure_builder.init_residue(resname, hetatm_flag, int_resseq,
                    icode)
            elif current_residue_id!=resseq:
                current_residue_id=resseq
                icode, int_resseq=self._get_icode(resseq)
                structure_builder.init_residue(resname, hetatm_flag, int_resseq,
                    icode)
            coord=numpy.array((x, y, z), 'f')
            element = element_list[i] if element_list else None
            structure_builder.init_atom(name, coord, tempfactor, occupancy, altloc,
                name, element=element)
            if aniso_flag==1:
                u=(aniso_u11[i], aniso_u12[i], aniso_u13[i],
                    aniso_u22[i], aniso_u23[i], aniso_u33[i])
                mapped_anisou=map(float, u)
                anisou_array=numpy.array(mapped_anisou, 'f')
                structure_builder.set_anisou(anisou_array)
        # Now try to set the cell
        try:
            a=float(mmcif_dict["_cell.length_a"])
            b=float(mmcif_dict["_cell.length_b"])
            c=float(mmcif_dict["_cell.length_c"])
            alpha=float(mmcif_dict["_cell.angle_alpha"])
            beta=float(mmcif_dict["_cell.angle_beta"])
            gamma=float(mmcif_dict["_cell.angle_gamma"])
            cell=numpy.array((a, b, c, alpha, beta, gamma), 'f')
            spacegroup=mmcif_dict["_symmetry.space_group_name_H-M"]
            spacegroup=spacegroup[1:-1] # get rid of quotes!!
            if spacegroup is None:
                raise Exception
            structure_builder.set_symmetry(spacegroup, cell)
        except:
            pass    # no cell found, so just ignore

    def _get_icode(self, resseq):
        """Tries to return the icode. In MMCIF files this is just part of
        resseq! In PDB files, it's a separate field."""
        last_resseq_char=resseq[-1]
        if last_resseq_char in ascii_letters:
            icode=last_resseq_char
            int_resseq=int(resseq[0:-1])
        else:
            icode=" "
            int_resseq=int(resseq)
        return icode, int_resseq
Beispiel #17
0
class MMCIFParser:
    """Parse a mmCIF file and return a Structure object."""
    def __init__(self, structure_builder=None, QUIET=False):
        """Create a PDBParser object.

        The mmCIF parser calls a number of standard methods in an aggregated
        StructureBuilder object. Normally this object is instanciated by the
        MMCIParser object itself, but if the user provides his/her own
        StructureBuilder object, the latter is used instead.

        Arguments:
         - structure_builder - an optional user implemented StructureBuilder class.
         - QUIET - Evaluated as a Boolean. If true, warnings issued in constructing
           the SMCRA data will be suppressed. If false (DEFAULT), they will be shown.
           These warnings might be indicative of problems in the mmCIF file!

        """
        if structure_builder is not None:
            self._structure_builder = structure_builder
        else:
            self._structure_builder = StructureBuilder()
        self.header = None
        # self.trailer = None
        self.line_counter = 0
        self.build_structure = None
        self.QUIET = bool(QUIET)

    # Public methods

    def get_structure(self, structure_id, filename):
        """Return the structure.

        Arguments:
         - structure_id - string, the id that will be used for the structure
         - filename - name of mmCIF file, OR an open text mode file handle

        """
        with warnings.catch_warnings():
            if self.QUIET:
                warnings.filterwarnings("ignore",
                                        category=PDBConstructionWarning)
            self._mmcif_dict = MMCIF2Dict(filename)
            self._build_structure(structure_id)
            self._structure_builder.set_header(self._get_header())

        return self._structure_builder.get_structure()

    # Private methods

    def _mmcif_get(self, key, dict, deflt):
        if key in dict:
            rslt = dict[key][0]
            if "?" != rslt:
                return rslt
        return deflt

    def _update_header_entry(self, target_key, keys):
        md = self._mmcif_dict
        for key in keys:
            val = md.get(key)
            try:
                item = val[0]
            except (TypeError, IndexError):
                continue
            if item != "?":
                self.header[target_key] = item
                break

    def _get_header(self):
        self.header = {
            "name": "",
            "head": "",
            "idcode": "",
            "deposition_date": "",
            "structure_method": "",
            "resolution": 0.0,
        }

        self._update_header_entry(
            "idcode", ["_entry_id", "_exptl.entry_id", "_struct.entry_id"])
        self._update_header_entry("name", ["_struct.title"])
        self._update_header_entry(
            "head",
            ["_struct_keywords.pdbx_keywords", "_struct_keywords.text"])
        self._update_header_entry(
            "deposition_date",
            ["_pdbx_database_status.recvd_initial_deposition_date"])
        self._update_header_entry("structure_method", ["_exptl.method"])
        self._update_header_entry(
            "resolution", ["_refine.ls_d_res_high", "_refine_hist.d_res_high"])
        self.header["resolution"] = float(self.header["resolution"])

        return self.header

    def _build_structure(self, structure_id):

        # two special chars as placeholders in the mmCIF format
        # for item values that cannot be explicitly assigned
        # see: pdbx/mmcif syntax web page
        _unassigned = {".", "?"}

        mmcif_dict = self._mmcif_dict
        atom_id_list = mmcif_dict["_atom_site.label_atom_id"]
        residue_id_list = mmcif_dict["_atom_site.label_comp_id"]
        try:
            element_list = mmcif_dict["_atom_site.type_symbol"]
        except KeyError:
            element_list = None
        chain_id_list = mmcif_dict["_atom_site.auth_asym_id"]
        x_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_x"]]
        y_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_y"]]
        z_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_z"]]
        alt_list = mmcif_dict["_atom_site.label_alt_id"]
        icode_list = mmcif_dict["_atom_site.pdbx_PDB_ins_code"]
        b_factor_list = mmcif_dict["_atom_site.B_iso_or_equiv"]
        occupancy_list = mmcif_dict["_atom_site.occupancy"]
        fieldname_list = mmcif_dict["_atom_site.group_PDB"]
        try:
            serial_list = [
                int(n) for n in mmcif_dict["_atom_site.pdbx_PDB_model_num"]
            ]
        except KeyError:
            # No model number column
            serial_list = None
        except ValueError:
            # Invalid model number (malformed file)
            raise PDBConstructionException("Invalid model number")
        try:
            aniso_u11 = mmcif_dict["_atom_site_anisotrop.U[1][1]"]
            aniso_u12 = mmcif_dict["_atom_site_anisotrop.U[1][2]"]
            aniso_u13 = mmcif_dict["_atom_site_anisotrop.U[1][3]"]
            aniso_u22 = mmcif_dict["_atom_site_anisotrop.U[2][2]"]
            aniso_u23 = mmcif_dict["_atom_site_anisotrop.U[2][3]"]
            aniso_u33 = mmcif_dict["_atom_site_anisotrop.U[3][3]"]
            aniso_flag = 1
        except KeyError:
            # no anisotropic B factors
            aniso_flag = 0
        # if auth_seq_id is present, we use this.
        # Otherwise label_seq_id is used.
        if "_atom_site.auth_seq_id" in mmcif_dict:
            seq_id_list = mmcif_dict["_atom_site.auth_seq_id"]
        else:
            seq_id_list = mmcif_dict["_atom_site.label_seq_id"]
        # Now loop over atoms and build the structure
        current_chain_id = None
        current_residue_id = None
        current_resname = None
        structure_builder = self._structure_builder
        structure_builder.init_structure(structure_id)
        structure_builder.init_seg(" ")
        # Historically, Biopython PDB parser uses model_id to mean array index
        # so serial_id means the Model ID specified in the file
        current_model_id = -1
        current_serial_id = -1
        for i in range(0, len(atom_id_list)):

            # set the line_counter for 'ATOM' lines only and not
            # as a global line counter found in the PDBParser()
            # this number should match the '_atom_site.id' index in the MMCIF
            structure_builder.set_line_counter(i)

            x = x_list[i]
            y = y_list[i]
            z = z_list[i]
            resname = residue_id_list[i]
            chainid = chain_id_list[i]
            altloc = alt_list[i]
            if altloc in _unassigned:
                altloc = " "
            int_resseq = int(seq_id_list[i])
            icode = icode_list[i]
            if icode in _unassigned:
                icode = " "
            name = atom_id_list[i]
            # occupancy & B factor
            try:
                tempfactor = float(b_factor_list[i])
            except ValueError:
                raise PDBConstructionException("Invalid or missing B factor")
            try:
                occupancy = float(occupancy_list[i])
            except ValueError:
                raise PDBConstructionException("Invalid or missing occupancy")
            fieldname = fieldname_list[i]
            if fieldname == "HETATM":
                if resname == "HOH" or resname == "WAT":
                    hetatm_flag = "W"
                else:
                    hetatm_flag = "H"
            else:
                hetatm_flag = " "

            resseq = (hetatm_flag, int_resseq, icode)

            if serial_list is not None:
                # model column exists; use it
                serial_id = serial_list[i]
                if current_serial_id != serial_id:
                    # if serial changes, update it and start new model
                    current_serial_id = serial_id
                    current_model_id += 1
                    structure_builder.init_model(current_model_id,
                                                 current_serial_id)
                    current_chain_id = None
                    current_residue_id = None
                    current_resname = None
            else:
                # no explicit model column; initialize single model
                structure_builder.init_model(current_model_id)

            if current_chain_id != chainid:
                current_chain_id = chainid
                structure_builder.init_chain(current_chain_id)
                current_residue_id = None
                current_resname = None

            if current_residue_id != resseq or current_resname != resname:
                current_residue_id = resseq
                current_resname = resname
                structure_builder.init_residue(resname, hetatm_flag,
                                               int_resseq, icode)

            coord = numpy.array((x, y, z), "f")
            element = element_list[i].upper() if element_list else None
            structure_builder.init_atom(name,
                                        coord,
                                        tempfactor,
                                        occupancy,
                                        altloc,
                                        name,
                                        element=element)
            if aniso_flag == 1 and i < len(aniso_u11):
                u = (
                    aniso_u11[i],
                    aniso_u12[i],
                    aniso_u13[i],
                    aniso_u22[i],
                    aniso_u23[i],
                    aniso_u33[i],
                )
                mapped_anisou = [float(_) for _ in u]
                anisou_array = numpy.array(mapped_anisou, "f")
                structure_builder.set_anisou(anisou_array)
        # Now try to set the cell
        try:
            a = float(mmcif_dict["_cell.length_a"][0])
            b = float(mmcif_dict["_cell.length_b"][0])
            c = float(mmcif_dict["_cell.length_c"][0])
            alpha = float(mmcif_dict["_cell.angle_alpha"][0])
            beta = float(mmcif_dict["_cell.angle_beta"][0])
            gamma = float(mmcif_dict["_cell.angle_gamma"][0])
            cell = numpy.array((a, b, c, alpha, beta, gamma), "f")
            spacegroup = mmcif_dict["_symmetry.space_group_name_H-M"][0]
            spacegroup = spacegroup[1:-1]  # get rid of quotes!!
            if spacegroup is None:
                raise Exception
            structure_builder.set_symmetry(spacegroup, cell)
        except Exception:
            pass  # no cell found, so just ignore
Beispiel #18
0
def pdb_extract(structure, **kwargs):

    # model to extract from pdb
    extract_model = None if not 'model' in kwargs else kwargs['model']
    new_model_id = -1 if not 'new_model' in kwargs else kwargs['new_model']
    extract_chain = None if not 'chain' in kwargs else kwargs['chain']
    first_res = None if not 'first_res' in kwargs else kwargs['first_res']
    last_res = None if not 'last_res' in kwargs else kwargs['last_res']
    new_first_res = None if not 'new_first_res' in kwargs else kwargs[
        'new_first_res']
    gap_count = 0 if not 'gap_count' in kwargs else kwargs['gap_count']
    water_id = None if not 'water' in kwargs else kwargs['water']

    model_rebumber_flag = bool((extract_model is not None)
                               or new_model_id >= 0)
    res_renumber_flag = bool(first_res or last_res or new_first_res
                             or gap_count)

    structure_builder = StructureBuilder()
    structure_builder.init_structure('pdb_extract')
    structure_builder.set_line_counter(0)
    line_counter = 0
    start_resseq_by_default = 1 if not new_first_res else new_first_res

    for model in structure:
        if model_rebumber_flag and \
                ( extract_model is not None ) and model.get_id() != extract_model:
            continue

        if model_rebumber_flag and new_model_id >= 0:
            this_model_id = new_model_id
            new_model_id += 1
        else:
            this_model_id = model.get_id()
        structure_builder.init_model(this_model_id, this_model_id)

        for chain in model:
            if extract_chain and chain.get_id() != extract_chain:
                continue
            structure_builder.init_seg(' ')
            structure_builder.init_chain(chain.get_id())
            resdict = {}
            if res_renumber_flag:
                # first_res = res_range_tuple[0]
                # last_res = res_range_tuple[1]

                resdict['before'] = select_residues_from_chain(
                    chain, first_res=first_res, gap_count=gap_count)
                resdict['hit'] = select_residues_from_chain(
                    chain, first_res=first_res, last_res=last_res)
                resdict['after'] = select_residues_from_chain(
                    chain, last_res=last_res, gap_count=gap_count)
            else:
                resdict['before'] = []
                resdict['hit'] = chain.get_list()
                resdict['after'] = []
            new_resseq = start_resseq_by_default - len(resdict['before'])
            resdict['water'] = chain_water_id(chain, water_id)
            for key in ['before', 'hit', 'after', 'water']:
                for residue in resdict[key]:
                    if res_renumber_flag:
                        new_resid = ' ', new_resseq, ' '
                    else:
                        new_resid = residue.get_id()
                    structure_builder.init_residue(residue.get_resname(),
                                                   *new_resid)
                    residue_atoms = None
                    if key == 'before':
                        residue_atoms = [atom for atom in residue if \
                                         (atom.get_name() == 'C' or atom.get_name() == 'O')]
                    elif key == 'hit' or key == 'water':
                        residue_atoms = residue.get_list()
                    elif key == 'after':
                        residue_atoms = [atom for atom in residue if \
                                         (atom.get_name() == 'N' or atom.get_name() == 'HN')]
                    for atom in residue_atoms:
                        structure_builder.init_atom(atom.get_name(),
                                                    atom.get_coord(),
                                                    atom.get_bfactor(),
                                                    atom.get_occupancy(),
                                                    atom.get_altloc(),
                                                    atom.get_fullname())
                        structure_builder.set_line_counter(line_counter)
                        line_counter += 1
                    new_resseq += 1
                    if key == 'water' and gap_count and len(
                            resdict['after']) != gap_count:
                        new_resseq += gap_count - len(resdict['after'])

    out_structure = structure_builder.get_structure()
    return out_structure
Beispiel #19
0
def read_PIC(
    file: TextIO,
    verbose: bool = False,
    quick: bool = False,
    defaults: bool = False,
) -> Structure:
    """Load Protein Internal Coordinate (.pic) data from file.

    PIC file format:
        - comment lines start with #
        - (optional) PDB HEADER record
           - idcode and deposition date recommended but optional
           - deposition date in PDB format or as changed by Biopython
        - (optional) PDB TITLE record
        - repeat:
           - Biopython Residue Full ID - sets residue IDs of returned structure
           - (optional) PDB N, CA, C ATOM records for chain start
           - (optional) PIC Hedra records for residue
           - (optional) PIC Dihedra records for residue
           - (optional) BFAC records listing AtomKeys and b-factors

    An improvement would define relative positions for HOH (water) entries.

    Defaults will be supplied for any value if defaults=True.  Default values
    are supplied in ic_data.py, but structures degrade quickly with any
    deviation from true coordinates.  Experiment with
    :data:`Bio.PDB.internal_coords.IC_Residue.pic_flags` options to
    :func:`write_PIC` to verify this.

    N.B. dihedron (i-1)C-N-CA-CB is ignored in assembly if O exists.

    C-beta is by default placed using O-C-CA-CB, but O is missing
    in some PDB file residues, which means the sidechain cannot be
    placed.  The alternate CB path (i-1)C-N-CA-CB is provided to
    circumvent this, but if this is needed then it must be adjusted in
    conjunction with PHI ((i-1)C-N-CA-C) as they overlap (see :meth:`.bond_set`
    and :meth:`.bond_rotate` to handle this automatically).

    :param Bio.File file: :func:`.as_handle` file name or handle
    :param bool verbose: complain when lines not as expected
    :param bool quick: don't check residues for all dihedra (no default values)
    :param bool defaults: create di/hedra as needed from reference database.
        Amide proton created if 'H' is in IC_Residue.accept_atoms
    :returns: Biopython Structure object, Residues with .internal_coord
        attributes but no coordinates except for chain start N, CA, C atoms if
        supplied, **OR** None on parse fail (silent unless verbose=True)

    """
    proton = "H" in IC_Residue.accept_atoms

    pdb_hdr_re = re.compile(
        r"^HEADER\s{4}(?P<cf>.{1,40})"
        r"(?:\s+(?P<dd>\d\d\d\d-\d\d-\d\d|\d\d-\w\w\w-\d\d))?"
        r"(?:\s+(?P<id>[0-9A-Z]{4}))?\s*$")
    pdb_ttl_re = re.compile(r"^TITLE\s{5}(?P<ttl>.+)\s*$")
    biop_id_re = re.compile(r"^\('(?P<pid>[^\s]*)',\s(?P<mdl>\d+),\s"
                            r"'(?P<chn>\s|\w)',\s\('(?P<het>\s|[\w\s-]+)"
                            r"',\s(?P<pos>-?\d+),\s'(?P<icode>\s|\w)'\)\)"
                            r"\s+(?P<res>[\w]{1,3})"
                            r"(\s\[(?P<segid>[a-zA-z\s]+)\])?"
                            r"\s*$")
    pdb_atm_re = re.compile(r"^ATOM\s\s(?:\s*(?P<ser>\d+))\s(?P<atm>[\w\s]{4})"
                            r"(?P<alc>\w|\s)(?P<res>[\w]{3})\s(?P<chn>.)"
                            r"(?P<pos>[\s\-\d]{4})(?P<icode>[A-Za-z\s])\s\s\s"
                            r"(?P<x>[\s\-\d\.]{8})(?P<y>[\s\-\d\.]{8})"
                            r"(?P<z>[\s\-\d\.]{8})(?P<occ>[\s\d\.]{6})"
                            r"(?P<tfac>[\s\d\.]{6})\s{6}"
                            r"(?P<segid>[a-zA-z\s]{4})(?P<elm>.{2})"
                            r"(?P<chg>.{2})?\s*$")
    bfac_re = re.compile(r"^BFAC:\s([^\s]+\s+[\-\d\.]+)"
                         r"\s*([^\s]+\s+[\-\d\.]+)?"
                         r"\s*([^\s]+\s+[\-\d\.]+)?"
                         r"\s*([^\s]+\s+[\-\d\.]+)?"
                         r"\s*([^\s]+\s+[\-\d\.]+)?")
    bfac2_re = re.compile(r"([^\s]+)\s+([\-\d\.]+)")
    struct_builder = StructureBuilder()

    # init empty header dict
    # - could use to parse HEADER and TITLE lines except
    #   deposition_date format changed from original PDB header
    header_dict = _parse_pdb_header_list([])

    curr_SMCS = [None, None, None, None]  # struct model chain seg
    SMCS_init = [
        struct_builder.init_structure,
        struct_builder.init_model,
        struct_builder.init_chain,
        struct_builder.init_seg,
    ]

    sb_res = None
    rkl = None
    sb_chain = None
    sbcic = None
    sbric = None

    akc = {}
    hl12 = {}
    ha = {}
    hl23 = {}
    da = {}
    bfacs = {}

    orphan_aks = set()  # []

    tr = []  # this residue
    pr = []  # previous residue

    def akcache(akstr: str) -> AtomKey:
        """Maintain dictionary of AtomKeys seen while reading this PIC file."""
        # akstr: full AtomKey string read from .pic file, includes residue info
        try:
            return akc[akstr]
        except (KeyError):
            ak = akc[akstr] = AtomKey(akstr)
            return ak

    def link_residues(ppr: List[Residue], pr: List[Residue]) -> None:
        """Set next and prev links between i-1 and i-2 residues."""
        for p_r in pr:
            pric = p_r.internal_coord
            for p_p_r in ppr:
                ppric = p_p_r.internal_coord
                if p_r.id[0] == " ":  # not heteroatoms
                    if pric not in ppric.rnext:
                        ppric.rnext.append(pric)
                if p_p_r.id[0] == " ":
                    if ppric not in pric.rprev:
                        pric.rprev.append(ppric)

    def process_hedron(
        a1: str,
        a2: str,
        a3: str,
        l12: str,
        ang: str,
        l23: str,
        ric: IC_Residue,
    ) -> Tuple:
        """Create Hedron on current (sbcic) Chain.internal_coord."""
        ek = (akcache(a1), akcache(a2), akcache(a3))
        atmNdx = AtomKey.fields.atm
        accpt = IC_Residue.accept_atoms
        if not all(ek[i].akl[atmNdx] in accpt for i in range(3)):
            return
        hl12[ek] = float(l12)
        ha[ek] = float(ang)
        hl23[ek] = float(l23)
        sbcic.hedra[ek] = ric.hedra[ek] = h = Hedron(ek)
        h.cic = sbcic
        ak_add(ek, ric)
        return ek

    def default_hedron(ek: Tuple, ric: IC_Residue) -> None:
        """Create Hedron based on same rdh_class hedra in ref database.

        Adds Hedron to current Chain.internal_coord, see ic_data for default
        values and reference database source.
        """
        aks = []
        hkey = None

        atmNdx = AtomKey.fields.atm
        resNdx = AtomKey.fields.resname
        resPos = AtomKey.fields.respos
        aks = [ek[i].akl for i in range(3)]

        atpl = tuple([aks[i][atmNdx] for i in range(3)])
        res = aks[0][resNdx]
        if (aks[0][resPos] !=
                aks[2][resPos]  # hedra crosses amide bond so not reversed
                or atpl == ("N", "CA", "C")  # or chain start tau
                or
                atpl in ic_data_backbone  # or found forward hedron in ic_data
                or
            (res not in ["A", "G"] and atpl in ic_data_sidechains[res])):
            hkey = ek
            rhcl = [aks[i][resNdx] + aks[i][atmNdx] for i in range(3)]
            try:
                dflts = hedra_defaults["".join(rhcl)][0]
            except KeyError:
                if aks[0][resPos] == aks[1][resPos]:
                    rhcl = [aks[i][resNdx] + aks[i][atmNdx] for i in range(2)]
                    rhc = "".join(rhcl) + "X" + aks[2][atmNdx]
                else:
                    rhcl = [
                        aks[i][resNdx] + aks[i][atmNdx] for i in range(1, 3)
                    ]
                    rhc = "X" + aks[0][atmNdx] + "".join(rhcl)
                dflts = hedra_defaults[rhc][0]
        else:
            # must be reversed or fail
            hkey = ek[::-1]
            rhcl = [aks[i][resNdx] + aks[i][atmNdx] for i in range(2, -1, -1)]
            dflts = hedra_defaults["".join(rhcl)][0]

        process_hedron(
            str(hkey[0]),
            str(hkey[1]),
            str(hkey[2]),
            dflts[0],
            dflts[1],
            dflts[2],
            ric,
        )

        if verbose:
            print(f" default for {ek}")

    def hedra_check(dk: str, ric: IC_Residue) -> None:
        """Confirm both hedra present for dihedron key, use default if set."""
        if dk[0:3] not in sbcic.hedra and dk[2::-1] not in sbcic.hedra:
            if defaults:
                default_hedron(dk[0:3], ric)
            else:
                print(f"{dk} missing h1")
        if dk[1:4] not in sbcic.hedra and dk[3:0:-1] not in sbcic.hedra:
            if defaults:
                default_hedron(dk[1:4], ric)
            else:
                print(f"{dk} missing h2")

    def process_dihedron(a1: str, a2: str, a3: str, a4: str, dangle: str,
                         ric: IC_Residue) -> Set:
        """Create Dihedron on current Chain.internal_coord."""
        ek = (
            akcache(a1),
            akcache(a2),
            akcache(a3),
            akcache(a4),
        )
        atmNdx = AtomKey.fields.atm
        accpt = IC_Residue.accept_atoms
        if not all(ek[i].akl[atmNdx] in accpt for i in range(4)):
            return
        da[ek] = float(dangle)
        sbcic.dihedra[ek] = ric.dihedra[ek] = d = Dihedron(ek)
        d.cic = sbcic
        if not quick:
            hedra_check(ek, ric)
        ak_add(ek, ric)
        return ek

    def default_dihedron(ek: List, ric: IC_Residue) -> None:
        """Create Dihedron based on same residue class dihedra in ref database.

        Adds Dihedron to current Chain.internal_coord, see ic_data for default
        values and reference database source.
        """
        atmNdx = AtomKey.fields.atm
        resNdx = AtomKey.fields.resname
        resPos = AtomKey.fields.respos

        rdclass = ""
        dclass = ""
        for ak in ek:
            dclass += ak.akl[atmNdx]
            rdclass += ak.akl[resNdx] + ak.akl[atmNdx]
        if dclass == "NCACN":
            rdclass = rdclass[0:7] + "XN"
        elif dclass == "CACNCA":
            rdclass = "XCAXC" + rdclass[5:]
        elif dclass == "CNCAC":
            rdclass = "XC" + rdclass[2:]
        if rdclass in dihedra_primary_defaults:
            process_dihedron(
                str(ek[0]),
                str(ek[1]),
                str(ek[2]),
                str(ek[3]),
                dihedra_primary_defaults[rdclass][0],
                ric,
            )

            if verbose:
                print(f" default for {ek}")

        elif rdclass in dihedra_secondary_defaults:
            primAngle, offset = dihedra_secondary_defaults[rdclass]
            rname = ek[2].akl[resNdx]
            rnum = int(ek[2].akl[resPos])
            paKey = None
            if primAngle == ("N", "CA", "C", "N") and ek[0].ric.rnext != []:
                paKey = [
                    AtomKey((rnum, None, rname, primAngle[x], None, None))
                    for x in range(3)
                ]
                rnext = ek[0].ric.rnext
                paKey.append(
                    AtomKey((
                        rnext[0].rbase[0],
                        None,
                        rnext[0].rbase[2],
                        "N",
                        None,
                        None,
                    )))
                paKey = tuple(paKey)
            elif primAngle == ("CA", "C", "N", "CA"):
                prname = pr.akl[0][resNdx]
                prnum = pr.akl[0][resPos]
                paKey = [
                    AtomKey(prnum, None, prname, primAngle[x], None, None)
                    for x in range(0, 2)
                ]
                paKey.add([
                    AtomKey((rnum, None, rname, primAngle[x], None, None))
                    for x in range(2, 4)
                ])
                paKey = tuple(paKey)
            else:
                paKey = tuple(
                    AtomKey((rnum, None, rname, atm, None, None))
                    for atm in primAngle)

            if paKey in da:
                process_dihedron(
                    str(ek[0]),
                    str(ek[1]),
                    str(ek[2]),
                    str(ek[3]),
                    da[paKey] + dihedra_secondary_defaults[rdclass][1],
                    ric,
                )

                if verbose:
                    print(f" secondary default for {ek}")

            elif rdclass in dihedra_secondary_xoxt_defaults:
                if primAngle == ("C", "N", "CA", "C"):  # primary for alt cb
                    # no way to trigger alt cb with default=True
                    # because will generate default N-CA-C-O
                    prname = pr.akl[0][resNdx]
                    prnum = pr.akl[0][resPos]
                    paKey = [
                        AtomKey(prnum, None, prname, primAngle[0], None, None)
                    ]
                    paKey.add([
                        AtomKey((rnum, None, rname, primAngle[x], None, None))
                        for x in range(1, 4)
                    ])
                    paKey = tuple(paKey)
                else:
                    primAngle, offset = dihedra_secondary_xoxt_defaults[
                        rdclass]
                    rname = ek[2].akl[resNdx]
                    rnum = int(ek[2].akl[resPos])
                    paKey = tuple(
                        AtomKey((rnum, None, rname, atm, None, None))
                        for atm in primAngle)

                if paKey in da:
                    process_dihedron(
                        str(ek[0]),
                        str(ek[1]),
                        str(ek[2]),
                        str(ek[3]),
                        da[paKey] + offset,
                        ric,
                    )

                    if verbose:
                        print(f" oxt default for {ek}")

                else:
                    print(f"missing primary angle {paKey} {primAngle} to "
                          f"generate {rnum}{rname} {rdclass}")
        else:
            print(
                f"missing {ek} -> {rdclass} ({dclass}) not found in primary or"
                " secondary defaults")

    def dihedra_check(ric: IC_Residue) -> None:
        """Look for required dihedra in residue, generate defaults if set."""

        # rnext should be set
        def ake_recurse(akList: List) -> List:
            """Bulid combinatorics of AtomKey lists."""
            car = akList[0]
            if len(akList) > 1:
                retList = []
                for ak in car:
                    cdr = akList[1:]
                    rslt = ake_recurse(cdr)
                    for r in rslt:
                        r.insert(0, ak)
                        retList.append(r)
                return retList
            else:
                if len(car) == 1:
                    return [list(car)]
                else:
                    retList = [[ak] for ak in car]
                    return retList

        def ak_expand(eLst: List) -> List:
            """Expand AtomKey list with altlocs, all combinatorics."""
            retList = []
            for edron in eLst:
                newList = []
                for ak in edron:
                    rslt = ak.ric.split_akl([ak])
                    rlst = [r[0] for r in rslt]
                    if rlst != []:
                        newList.append(rlst)
                    else:
                        newList.append([ak])
                rslt = ake_recurse(newList)
                for r in rslt:
                    retList.append(r)
            return retList

        # dihedra_check processing starts here
        # generate the list of dihedra this residue should have
        chkLst = []
        sN, sCA, sC = AtomKey(ric, "N"), AtomKey(ric, "CA"), AtomKey(ric, "C")
        sO, sCB, sH = AtomKey(ric, "O"), AtomKey(ric, "CB"), AtomKey(ric, "H")
        if ric.rnext != []:
            for rn in ric.rnext:
                nN, nCA, nC = (
                    AtomKey(rn, "N"),
                    AtomKey(rn, "CA"),
                    AtomKey(rn, "C"),
                )
                # intermediate residue, need psi, phi, omg
                chkLst.append((sN, sCA, sC, nN))  # psi
                chkLst.append((sCA, sC, nN, nCA))  # omg i+1
                chkLst.append((sC, nN, nCA, nC))  # phi i+1
        else:
            chkLst.append((sN, sCA, sC, AtomKey(ric, "OXT")))  # psi
            rn = "(no rnext)"

        chkLst.append((sN, sCA, sC, sO))  # locate backbone O
        if ric.lc != "G":
            chkLst.append((sO, sC, sCA, sCB))  # locate CB
        if ric.rprev != [] and ric.lc != "P" and proton:
            chkLst.append((sC, sCA, sN, sH))  # amide proton

        try:
            for edron in ic_data_sidechains[ric.lc]:
                if len(edron) > 3:  # dihedra only
                    if all(not atm[0] == "H" for atm in edron):
                        akl = [AtomKey(ric, atm) for atm in edron[0:4]]
                        chkLst.append(akl)
        except KeyError:
            pass

        # now compare generated list to ric.dihedra, get defaults if set.
        chkLst = ak_expand(chkLst)
        altloc_ndx = AtomKey.fields.altloc

        for dk in chkLst:
            if tuple(dk) in ric.dihedra:
                pass
            elif sH in dk:
                pass  # ignore missing hydrogens
            elif all(atm.akl[altloc_ndx] is None for atm in dk):
                if defaults:
                    default_dihedron(dk, ric)
                else:
                    if verbose:
                        print(f"{ric}-{rn} missing {dk}")
            else:
                # print(f"skip {ek}")
                pass  # ignore missing combinatoric of altloc atoms
                # need more here?

    def ak_add(ek: set, ric: IC_Residue) -> None:
        """Allocate edron key AtomKeys to current residue as appropriate.

        A hedron or dihedron may span a backbone amide bond, this routine
        allocates atoms in the (h/di)edron to the ric residue or saves them
        for a residue yet to be processed.

        :param set ek: AtomKeys in edron
        :param IC_Residue ric: current residue to assign AtomKeys to
        """
        res = ric.residue
        reskl = (
            str(res.id[1]),
            (None if res.id[2] == " " else res.id[2]),
            ric.lc,
        )
        for ak in ek:
            if ak.ric is None:
                sbcic.akset.add(ak)
                if ak.akl[0:3] == reskl:
                    ak.ric = ric
                    ric.ak_set.add(ak)
                else:
                    orphan_aks.add(ak)

    def finish_chain() -> None:
        """Do last rnext, rprev links and process chain edra data."""
        link_residues(pr, tr)
        # check/confirm completeness
        if not quick:
            for r in pr:
                dihedra_check(r.internal_coord)
            for r in tr:
                dihedra_check(r.internal_coord)

        if ha != {}:
            sha = {k: ha[k] for k in sorted(ha)}
            shl12 = {k: hl12[k] for k in sorted(hl12)}
            shl23 = {k: hl23[k] for k in sorted(hl23)}
            sbcic._hedraDict2chain(shl12, sha, shl23, da, bfacs)

    # read_PIC processing starts here:
    with as_handle(file, mode="r") as handle:
        for line in handle.readlines():
            if line.startswith("#"):
                pass  # skip comment lines
            elif line.startswith("HEADER "):
                m = pdb_hdr_re.match(line)
                if m:
                    header_dict["head"] = m.group("cf")  # classification
                    header_dict["idcode"] = m.group("id")
                    header_dict["deposition_date"] = m.group("dd")
                elif verbose:
                    print("Reading pic file", file, "HEADER parse fail: ",
                          line)
            elif line.startswith("TITLE "):
                m = pdb_ttl_re.match(line)
                if m:
                    header_dict["name"] = m.group("ttl").strip()
                    # print('TTL: ', m.group('ttl').strip())
                elif verbose:
                    print("Reading pic file", file, "TITLE parse fail:, ",
                          line)
            elif line.startswith("("):  # Biopython ID line for Residue
                m = biop_id_re.match(line)
                if m:
                    # check SMCS = Structure, Model, Chain, SegID
                    segid = m.group(9)
                    if segid is None:
                        segid = "    "
                    this_SMCS = [
                        m.group(1),
                        int(m.group(2)),
                        m.group(3),
                        segid,
                    ]
                    if curr_SMCS != this_SMCS:
                        if curr_SMCS[:3] != this_SMCS[:3] and ha != {}:
                            # chain change so process current chain data
                            finish_chain()

                            akc = {}  # atomkey cache, used by akcache()
                            hl12 = {}  # hedra key -> len12
                            ha = {}  # -> hedra angle
                            hl23 = {}  # -> len23
                            da = {}  # dihedra key -> angle value
                            bfacs = {}  # atomkey string -> b-factor
                        # init new Biopython SMCS level as needed
                        for i in range(4):
                            if curr_SMCS[i] != this_SMCS[i]:
                                SMCS_init[i](this_SMCS[i])
                                curr_SMCS[i] = this_SMCS[i]
                                if i == 0:
                                    # 0 = init structure so add header
                                    struct_builder.set_header(header_dict)
                                elif i == 1:
                                    # new model means new chain and new segid
                                    curr_SMCS[2] = curr_SMCS[3] = None
                                elif i == 2:
                                    # new chain so init internal_coord
                                    sb_chain = struct_builder.chain
                                    sbcic = sb_chain.internal_coord = IC_Chain(
                                        sb_chain)

                    struct_builder.init_residue(
                        m.group("res"),
                        m.group("het"),
                        int(m.group("pos")),
                        m.group("icode"),
                    )

                    sb_res = struct_builder.residue
                    if sb_res.id[0] != " ":  # skip hetatm
                        continue
                    if 2 == sb_res.is_disordered():
                        for r in sb_res.child_dict.values():
                            if not r.internal_coord:
                                sb_res = r
                                break
                        # added to disordered res
                        tr.append(sb_res)
                    else:
                        # new res so fix up previous residue as feasible
                        link_residues(pr, tr)

                        if not quick:
                            for r in pr:
                                # create di/hedra if default for residue i-1
                                # just linked
                                dihedra_check(r.internal_coord)

                        pr = tr
                        tr = [sb_res]

                    sbric = sb_res.internal_coord = IC_Residue(
                        sb_res)  # no atoms so no rak
                    sbric.cic = sbcic
                    rkl = (
                        str(sb_res.id[1]),
                        (None if sb_res.id[2] == " " else sb_res.id[2]),
                        sbric.lc,
                    )
                    sbcic.ordered_aa_ic_list.append(sbric)

                    # update AtomKeys w/o IC_Residue references, in case
                    # chain ends before di/hedra sees them (2XHE test case)
                    for ak in orphan_aks:
                        if ak.akl[0:3] == rkl:
                            ak.ric = sbric
                            sbric.ak_set.add(ak)
                            # may need altoc support here
                    orphan_aks = set(
                        filter(lambda ak: ak.ric is None, orphan_aks))

                else:
                    if verbose:
                        print(
                            "Reading pic file",
                            file,
                            "residue ID parse fail: ",
                            line,
                        )
                    return None
            elif line.startswith("ATOM "):
                m = pdb_atm_re.match(line)
                if m:
                    if sb_res is None:
                        # ATOM without res spec already loaded, not a pic file
                        if verbose:
                            print(
                                "Reading pic file",
                                file,
                                "ATOM without residue configured:, ",
                                line,
                            )
                        return None
                    if sb_res.resname != m.group("res") or sb_res.id[1] != int(
                            m.group("pos")):
                        if verbose:
                            print(
                                "Reading pic file",
                                file,
                                "ATOM not in configured residue (",
                                sb_res.resname,
                                str(sb_res.id),
                                "):",
                                line,
                            )
                        return None
                    coord = numpy.array(
                        (
                            float(m.group("x")),
                            float(m.group("y")),
                            float(m.group("z")),
                        ),
                        "f",
                    )
                    struct_builder.init_atom(
                        m.group("atm").strip(),
                        coord,
                        float(m.group("tfac")),
                        float(m.group("occ")),
                        m.group("alc"),
                        m.group("atm"),
                        int(m.group("ser")),
                        m.group("elm").strip(),
                    )

                    # reset because prev does not link to this residue
                    # (chainBreak)
                    pr = []

            elif line.startswith("BFAC: "):
                m = bfac_re.match(line)
                if m:
                    for bfac_pair in m.groups():
                        if bfac_pair is not None:
                            m2 = bfac2_re.match(bfac_pair)
                            bfacs[m2.group(1)] = float(m2.group(2))
                # else:
                #    print f"Reading pic file {file} B-factor fail: {line}"
            else:
                m = Edron.edron_re.match(line)
                if m and sb_res is not None:
                    if m["a4"] is None:
                        process_hedron(
                            m["a1"],
                            m["a2"],
                            m["a3"],
                            m["len12"],
                            m["angle"],
                            m["len23"],
                            sb_res.internal_coord,
                        )
                    else:
                        process_dihedron(
                            m["a1"],
                            m["a2"],
                            m["a3"],
                            m["a4"],
                            float(m["dihedral"]),
                            sb_res.internal_coord,
                        )

                elif m:
                    print(
                        "PIC file: ",
                        file,
                        " error: no residue info before reading (di/h)edron: ",
                        line,
                    )
                    return None
                elif line.strip():
                    if verbose:
                        print(
                            "Reading PIC file",
                            file,
                            "parse fail on: .",
                            line,
                            ".",
                        )
                    return None

    # reached end of input
    finish_chain()

    # print(report_PIC(struct_builder.get_structure()))
    return struct_builder.get_structure()
class MMCIFParser:
    def get_structure(self, structure_id, filename):
        self._mmcif_dict = MMCIF2Dict(filename)
        self._structure_builder = StructureBuilder()
        self._build_structure(structure_id)
        return self._structure_builder.get_structure()

    def _build_structure(self, structure_id):
        mmcif_dict = self._mmcif_dict
        atom_id_list = mmcif_dict["_atom_site.label_atom_id"]
        residue_id_list = mmcif_dict["_atom_site.label_comp_id"]
        seq_id_list = mmcif_dict["_atom_site.label_seq_id"]
        chain_id_list = mmcif_dict["_atom_site.label_asym_id"]
        x_list = map(float, mmcif_dict["_atom_site.Cartn_x"])
        y_list = map(float, mmcif_dict["_atom_site.Cartn_y"])
        z_list = map(float, mmcif_dict["_atom_site.Cartn_z"])
        alt_list = mmcif_dict["_atom_site.label_alt_id"]
        b_factor_list = mmcif_dict["_atom_site.B_iso_or_equiv"]
        occupancy_list = mmcif_dict["_atom_site.occupancy"]
        fieldname_list = mmcif_dict["_atom_site.group_PDB"]
        try:
            aniso_u11 = mmcif_dict["_atom_site.aniso_U[1][1]"]
            aniso_u12 = mmcif_dict["_atom_site.aniso_U[1][2]"]
            aniso_u13 = mmcif_dict["_atom_site.aniso_U[1][3]"]
            aniso_u22 = mmcif_dict["_atom_site.aniso_U[2][2]"]
            aniso_u23 = mmcif_dict["_atom_site.aniso_U[2][3]"]
            aniso_u33 = mmcif_dict["_atom_site.aniso_U[3][3]"]
            aniso_flag = 1
        except KeyError:
            # no anisotropic B factors
            aniso_flag = 0
        # if auth_seq_id is present, we use this.
        # Otherwise label_seq_id is used.
        if "_atom_site.auth_seq_id" in mmcif_dict:
            seq_id_list = mmcif_dict["_atom_site.auth_seq_id"]
        else:
            seq_id_list = mmcif_dict["_atom_site.label_seq_id"]
        # Now loop over atoms and build the structure
        current_chain_id = None
        current_residue_id = None
        current_model_id = 0
        structure_builder = self._structure_builder
        structure_builder.init_structure(structure_id)
        structure_builder.init_model(current_model_id)
        structure_builder.init_seg(" ")
        for i in xrange(0, len(atom_id_list)):
            x = x_list[i]
            y = y_list[i]
            z = z_list[i]
            resname = residue_id_list[i]
            chainid = chain_id_list[i]
            altloc = alt_list[i]
            if altloc == ".":
                altloc = " "
            resseq = seq_id_list[i]
            name = atom_id_list[i]
            tempfactor = b_factor_list[i]
            occupancy = occupancy_list[i]
            fieldname = fieldname_list[i]
            if fieldname == "HETATM":
                hetatm_flag = "H"
            else:
                hetatm_flag = " "
            if current_chain_id != chainid:
                current_chain_id = chainid
                structure_builder.init_chain(current_chain_id)
                current_residue_id = resseq
                icode, int_resseq = self._get_icode(resseq)
                structure_builder.init_residue(resname, hetatm_flag,
                                               int_resseq, icode)
            elif current_residue_id != resseq:
                current_residue_id = resseq
                icode, int_resseq = self._get_icode(resseq)
                structure_builder.init_residue(resname, hetatm_flag,
                                               int_resseq, icode)
            coord = numpy.array((x, y, z), 'f')
            structure_builder.init_atom(name, coord, tempfactor, occupancy,
                                        altloc, name)
            if aniso_flag == 1:
                u = (aniso_u11[i], aniso_u12[i], aniso_u13[i], aniso_u22[i],
                     aniso_u23[i], aniso_u33[i])
                mapped_anisou = map(float, u)
                anisou_array = numpy.array(mapped_anisou, 'f')
                structure_builder.set_anisou(anisou_array)
        # Now try to set the cell
        try:
            a = float(mmcif_dict["_cell.length_a"])
            b = float(mmcif_dict["_cell.length_b"])
            c = float(mmcif_dict["_cell.length_c"])
            alpha = float(mmcif_dict["_cell.angle_alpha"])
            beta = float(mmcif_dict["_cell.angle_beta"])
            gamma = float(mmcif_dict["_cell.angle_gamma"])
            cell = numpy.array((a, b, c, alpha, beta, gamma), 'f')
            spacegroup = mmcif_dict["_symmetry.space_group_name_H-M"]
            spacegroup = spacegroup[1:-1]  # get rid of quotes!!
            if spacegroup == None:
                raise Exception
            structure_builder.set_symmetry(spacegroup, cell)
        except:
            pass  # no cell found, so just ignore

    def _get_icode(self, resseq):
        """Tries to return the icode. In MMCIF files this is just part of
        resseq! In PDB files, it's a separate field."""
        last_resseq_char = resseq[-1]
        if last_resseq_char in letters:
            icode = last_resseq_char
            int_resseq = int(resseq[0:-1])
        else:
            icode = " "
            int_resseq = int(resseq)
        return icode, int_resseq
Beispiel #21
0
class MMCIFParser(object):
    """Parse a PDB file and return a Structure object."""

    def __init__(self, structure_builder=None, QUIET=False):
        """Create a PDBParser object.

        The PDB parser call a number of standard methods in an aggregated
        StructureBuilder object. Normally this object is instanciated by the
        MMCIParser object itself, but if the user provides his/her own
        StructureBuilder object, the latter is used instead.

        Arguments:
         - structure_builder - an optional user implemented StructureBuilder class.
         - QUIET - Evaluated as a Boolean. If true, warnings issued in constructing
           the SMCRA data will be suppressed. If false (DEFAULT), they will be shown.
           These warnings might be indicative of problems in the PDB file!
        """
        if structure_builder is not None:
            self._structure_builder = structure_builder
        else:
            self._structure_builder = StructureBuilder()
        # self.header = None
        # self.trailer = None
        self.line_counter = 0
        self.build_structure = None
        self.QUIET = bool(QUIET)

    # Public methods

    def get_structure(self, structure_id, filename):
        """Return the structure.

        Arguments:
         - structure_id - string, the id that will be used for the structure
         - filename - name of the mmCIF file OR an open filehandle
        """
        with warnings.catch_warnings():
            if self.QUIET:
                warnings.filterwarnings("ignore", category=PDBConstructionWarning)
        self._mmcif_dict = MMCIF2Dict(filename)
        self._build_structure(structure_id)
        return self._structure_builder.get_structure()

    # Private methods

    def _build_structure(self, structure_id):
        mmcif_dict = self._mmcif_dict
        atom_id_list = mmcif_dict["_atom_site.label_atom_id"]
        residue_id_list = mmcif_dict["_atom_site.label_comp_id"]
        try:
            element_list = mmcif_dict["_atom_site.type_symbol"]
        except KeyError:
            element_list = None
        seq_id_list = mmcif_dict["_atom_site.label_seq_id"]
        chain_id_list = mmcif_dict["_atom_site.label_asym_id"]
        x_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_x"]]
        y_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_y"]]
        z_list = [float(x) for x in mmcif_dict["_atom_site.Cartn_z"]]
        alt_list = mmcif_dict["_atom_site.label_alt_id"]
        icode_list = mmcif_dict["_atom_site.pdbx_PDB_ins_code"]
        b_factor_list = mmcif_dict["_atom_site.B_iso_or_equiv"]
        occupancy_list = mmcif_dict["_atom_site.occupancy"]
        fieldname_list = mmcif_dict["_atom_site.group_PDB"]
        try:
            serial_list = [int(n) for n in mmcif_dict["_atom_site.pdbx_PDB_model_num"]]
        except KeyError:
            # No model number column
            serial_list = None
        except ValueError:
            # Invalid model number (malformed file)
            raise PDBConstructionException("Invalid model number")
        try:
            aniso_u11 = mmcif_dict["_atom_site.aniso_U[1][1]"]
            aniso_u12 = mmcif_dict["_atom_site.aniso_U[1][2]"]
            aniso_u13 = mmcif_dict["_atom_site.aniso_U[1][3]"]
            aniso_u22 = mmcif_dict["_atom_site.aniso_U[2][2]"]
            aniso_u23 = mmcif_dict["_atom_site.aniso_U[2][3]"]
            aniso_u33 = mmcif_dict["_atom_site.aniso_U[3][3]"]
            aniso_flag = 1
        except KeyError:
            # no anisotropic B factors
            aniso_flag = 0
        # if auth_seq_id is present, we use this.
        # Otherwise label_seq_id is used.
        if "_atom_site.auth_seq_id" in mmcif_dict:
            seq_id_list = mmcif_dict["_atom_site.auth_seq_id"]
        else:
            seq_id_list = mmcif_dict["_atom_site.label_seq_id"]
        # Now loop over atoms and build the structure
        current_chain_id = None
        current_residue_id = None
        structure_builder = self._structure_builder
        structure_builder.init_structure(structure_id)
        structure_builder.init_seg(" ")
        # Historically, Biopython PDB parser uses model_id to mean array index
        # so serial_id means the Model ID specified in the file
        current_model_id = -1
        current_serial_id = 0
        for i in range(0, len(atom_id_list)):

            # set the line_counter for 'ATOM' lines only and not
            # as a global line counter found in the PDBParser()
            # this number should match the '_atom_site.id' index in the MMCIF
            structure_builder.set_line_counter(i)

            x = x_list[i]
            y = y_list[i]
            z = z_list[i]
            resname = residue_id_list[i]
            chainid = chain_id_list[i]
            altloc = alt_list[i]
            if altloc == ".":
                altloc = " "
            resseq = seq_id_list[i]
            icode = icode_list[i]
            if icode == "?":
                icode = " "
            name = atom_id_list[i]
            # occupancy & B factor
            try:
                tempfactor = float(b_factor_list[i])
            except ValueError:
                raise PDBConstructionException("Invalid or missing B factor")
            try:
                occupancy = float(occupancy_list[i])
            except ValueError:
                raise PDBConstructionException("Invalid or missing occupancy")
            fieldname = fieldname_list[i]
            if fieldname == "HETATM":
                hetatm_flag = "H"
            else:
                hetatm_flag = " "
            if serial_list is not None:
                # model column exists; use it
                serial_id = serial_list[i]
                if current_serial_id != serial_id:
                    # if serial changes, update it and start new model
                    current_serial_id = serial_id
                    current_model_id += 1
                    structure_builder.init_model(current_model_id, current_serial_id)
                    current_chain_id = None
                    current_residue_id = None
            else:
                # no explicit model column; initialize single model
                structure_builder.init_model(current_model_id)

            if current_chain_id != chainid:
                current_chain_id = chainid
                structure_builder.init_chain(current_chain_id)

            if current_residue_id != resseq:
                current_residue_id = resseq
                int_resseq = int(resseq)
                structure_builder.init_residue(resname, hetatm_flag, int_resseq, icode)

            coord = numpy.array((x, y, z), 'f')
            element = element_list[i] if element_list else None
            structure_builder.init_atom(name, coord, tempfactor, occupancy, altloc,
                name, element=element)
            if aniso_flag == 1:
                u = (aniso_u11[i], aniso_u12[i], aniso_u13[i],
                    aniso_u22[i], aniso_u23[i], aniso_u33[i])
                mapped_anisou = [float(x) for x in u]
                anisou_array = numpy.array(mapped_anisou, 'f')
                structure_builder.set_anisou(anisou_array)
        # Now try to set the cell
        try:
            a = float(mmcif_dict["_cell.length_a"])
            b = float(mmcif_dict["_cell.length_b"])
            c = float(mmcif_dict["_cell.length_c"])
            alpha = float(mmcif_dict["_cell.angle_alpha"])
            beta = float(mmcif_dict["_cell.angle_beta"])
            gamma = float(mmcif_dict["_cell.angle_gamma"])
            cell = numpy.array((a, b, c, alpha, beta, gamma), 'f')
            spacegroup = mmcif_dict["_symmetry.space_group_name_H-M"]
            spacegroup = spacegroup[1:-1]  # get rid of quotes!!
            if spacegroup is None:
                raise Exception
            structure_builder.set_symmetry(spacegroup, cell)
        except Exception:
            pass    # no cell found, so just ignore
Beispiel #22
0
class MMCIFParser(object):
    def get_structure(self, structure_id, filename):
        self._mmcif_dict=MMCIF2Dict(filename)
        self._structure_builder=StructureBuilder()
        self._build_structure(structure_id)
        return self._structure_builder.get_structure()

    def _build_structure(self, structure_id):
        mmcif_dict=self._mmcif_dict
        atom_id_list=mmcif_dict["_atom_site.label_atom_id"]
        residue_id_list=mmcif_dict["_atom_site.label_comp_id"]
        seq_id_list=mmcif_dict["_atom_site.label_seq_id"]
        chain_id_list=mmcif_dict["_atom_site.label_asym_id"]
        x_list=map(float, mmcif_dict["_atom_site.Cartn_x"])
        y_list=map(float, mmcif_dict["_atom_site.Cartn_y"])
        z_list=map(float, mmcif_dict["_atom_site.Cartn_z"])
        alt_list=mmcif_dict["_atom_site.label_alt_id"]
        b_factor_list=mmcif_dict["_atom_site.B_iso_or_equiv"]
        occupancy_list=mmcif_dict["_atom_site.occupancy"]
        fieldname_list=mmcif_dict["_atom_site.group_PDB"]
        try:
            aniso_u11=mmcif_dict["_atom_site.aniso_U[1][1]"]
            aniso_u12=mmcif_dict["_atom_site.aniso_U[1][2]"]
            aniso_u13=mmcif_dict["_atom_site.aniso_U[1][3]"]
            aniso_u22=mmcif_dict["_atom_site.aniso_U[2][2]"]
            aniso_u23=mmcif_dict["_atom_site.aniso_U[2][3]"]
            aniso_u33=mmcif_dict["_atom_site.aniso_U[3][3]"]
            aniso_flag=1
        except KeyError:
            # no anisotropic B factors
            aniso_flag=0
        # if auth_seq_id is present, we use this.
        # Otherwise label_seq_id is used.
        if "_atom_site.auth_seq_id" in mmcif_dict:
            seq_id_list=mmcif_dict["_atom_site.auth_seq_id"]
        else:
            seq_id_list=mmcif_dict["_atom_site.label_seq_id"]
        # Now loop over atoms and build the structure
        current_chain_id=None
        current_residue_id=None
        current_model_id=0
        structure_builder=self._structure_builder
        structure_builder.init_structure(structure_id)
        structure_builder.init_model(current_model_id)
        structure_builder.init_seg(" ")
        for i in xrange(0, len(atom_id_list)):
            x=x_list[i]
            y=y_list[i]
            z=z_list[i]
            resname=residue_id_list[i]
            chainid=chain_id_list[i]
            altloc=alt_list[i]
            if altloc==".":
                altloc=" "
            resseq=seq_id_list[i]
            name=atom_id_list[i]
            tempfactor=b_factor_list[i]
            occupancy=occupancy_list[i]
            fieldname=fieldname_list[i]
            if fieldname=="HETATM":
                hetatm_flag="H"
            else:
                hetatm_flag=" "
            if current_chain_id!=chainid:
                current_chain_id=chainid
                structure_builder.init_chain(current_chain_id)
                current_residue_id=resseq
                icode, int_resseq=self._get_icode(resseq)
                structure_builder.init_residue(resname, hetatm_flag, int_resseq, 
                    icode)
            elif current_residue_id!=resseq:
                current_residue_id=resseq
                icode, int_resseq=self._get_icode(resseq)
                structure_builder.init_residue(resname, hetatm_flag, int_resseq, 
                    icode)
            coord=numpy.array((x, y, z), 'f')  
            structure_builder.init_atom(name, coord, tempfactor, occupancy, altloc,
                name)   
            if aniso_flag==1:
                u=(aniso_u11[i], aniso_u12[i], aniso_u13[i],
                    aniso_u22[i], aniso_u23[i], aniso_u33[i])
                mapped_anisou=map(float, u)
                anisou_array=numpy.array(mapped_anisou, 'f')
                structure_builder.set_anisou(anisou_array)
        # Now try to set the cell
        try:
            a=float(mmcif_dict["_cell.length_a"])
            b=float(mmcif_dict["_cell.length_b"])
            c=float(mmcif_dict["_cell.length_c"])
            alpha=float(mmcif_dict["_cell.angle_alpha"])
            beta=float(mmcif_dict["_cell.angle_beta"])
            gamma=float(mmcif_dict["_cell.angle_gamma"])
            cell=numpy.array((a, b, c, alpha, beta, gamma), 'f')
            spacegroup=mmcif_dict["_symmetry.space_group_name_H-M"]
            spacegroup=spacegroup[1:-1] # get rid of quotes!!
            if spacegroup==None:
                raise Exception
            structure_builder.set_symmetry(spacegroup, cell)
        except:
            pass    # no cell found, so just ignore

    def _get_icode(self, resseq):           
        """Tries to return the icode. In MMCIF files this is just part of
        resseq! In PDB files, it's a separate field."""
        last_resseq_char=resseq[-1]
        if last_resseq_char in letters:
            icode=last_resseq_char
            int_resseq=int(resseq[0:-1])
        else:
            icode=" "
            int_resseq=int(resseq)
        return icode, int_resseq    
Beispiel #23
0
class PDBParser(object):
    """Parse a PDB file and return a Structure object."""

    def __init__(self, PERMISSIVE=True, get_header=False,
                 structure_builder=None, QUIET=False):
        """Create a PDBParser object.

        The PDB parser call a number of standard methods in an aggregated
        StructureBuilder object. Normally this object is instanciated by the
        PDBParser object itself, but if the user provides his/her own
        StructureBuilder object, the latter is used instead.

        Arguments:
         - PERMISSIVE - Evaluated as a Boolean. If false, exceptions in
           constructing the SMCRA data structure are fatal. If true (DEFAULT),
           the exceptions are caught, but some residues or atoms will be missing.
           THESE EXCEPTIONS ARE DUE TO PROBLEMS IN THE PDB FILE!.
         - structure_builder - an optional user implemented StructureBuilder class.
         - QUIET - Evaluated as a Boolean. If true, warnings issued in constructing
           the SMCRA data will be suppressed. If false (DEFAULT), they will be shown.
           These warnings might be indicative of problems in the PDB file!
        """
        if structure_builder is not None:
            self.structure_builder = structure_builder
        else:
            self.structure_builder = StructureBuilder()
        self.header = None
        self.trailer = None
        self.line_counter = 0
        self.PERMISSIVE = bool(PERMISSIVE)
        self.QUIET = bool(QUIET)

    # Public methods

    def get_structure(self, id, file):
        """Return the structure.

        Arguments:
         - id - string, the id that will be used for the structure
         - file - name of the PDB file OR an open filehandle
        """
        with warnings.catch_warnings():
            if self.QUIET:
                warnings.filterwarnings("ignore", category=PDBConstructionWarning)

            self.header = None
            self.trailer = None
            # Make a StructureBuilder instance (pass id of structure as parameter)
            self.structure_builder.init_structure(id)

            with as_handle(file, mode='rU') as handle:
                self._parse(handle.readlines())

            self.structure_builder.set_header(self.header)
            # Return the Structure instance
            structure = self.structure_builder.get_structure()

        return structure

    def get_header(self):
        """Return the header."""
        return self.header

    def get_trailer(self):
        """Return the trailer."""
        return self.trailer

    # Private methods

    def _parse(self, header_coords_trailer):
        """Parse the PDB file (PRIVATE)."""
        # Extract the header; return the rest of the file
        self.header, coords_trailer = self._get_header(header_coords_trailer)
        # Parse the atomic data; return the PDB file trailer
        self.trailer = self._parse_coordinates(coords_trailer)

    def _get_header(self, header_coords_trailer):
        """Get the header of the PDB file, return the rest (PRIVATE)."""
        structure_builder = self.structure_builder
        i = 0
        for i in range(0, len(header_coords_trailer)):
            structure_builder.set_line_counter(i + 1)
            line = header_coords_trailer[i]
            record_type = line[0:6]
            if record_type == "ATOM  " or record_type == "HETATM" or record_type == "MODEL ":
                break
        header = header_coords_trailer[0:i]
        # Return the rest of the coords+trailer for further processing
        self.line_counter = i
        coords_trailer = header_coords_trailer[i:]
        header_dict = _parse_pdb_header_list(header)
        return header_dict, coords_trailer

    def _parse_coordinates(self, coords_trailer):
        """Parse the atomic data in the PDB file (PRIVATE)."""
        local_line_counter = 0
        structure_builder = self.structure_builder
        current_model_id = 0
        # Flag we have an open model
        model_open = 0
        current_chain_id = None
        current_segid = None
        current_residue_id = None
        current_resname = None
        for i in range(0, len(coords_trailer)):
            line = coords_trailer[i].rstrip('\n')
            record_type = line[0:6]
            global_line_counter = self.line_counter + local_line_counter + 1
            structure_builder.set_line_counter(global_line_counter)
            if record_type == "ATOM  " or record_type == "HETATM":
                # Initialize the Model - there was no explicit MODEL record
                if not model_open:
                    structure_builder.init_model(current_model_id)
                    current_model_id += 1
                    model_open = 1
                fullname = line[12:16]
                # get rid of whitespace in atom names
                split_list = fullname.split()
                if len(split_list) != 1:
                    # atom name has internal spaces, e.g. " N B ", so
                    # we do not strip spaces
                    name = fullname
                else:
                    # atom name is like " CA ", so we can strip spaces
                    name = split_list[0]
                altloc = line[16]
                resname = line[17:20]
                chainid = line[21]
                try:
                    serial_number = int(line[6:11])
                except Exception:
                    serial_number = 0
                resseq = int(line[22:26].split()[0])  # sequence identifier
                icode = line[26]  # insertion code
                if record_type == "HETATM":  # hetero atom flag
                    if resname == "HOH" or resname == "WAT":
                        hetero_flag = "W"
                    else:
                        hetero_flag = "H"
                else:
                    hetero_flag = " "
                residue_id = (hetero_flag, resseq, icode)
                # atomic coordinates
                try:
                    x = float(line[30:38])
                    y = float(line[38:46])
                    z = float(line[46:54])
                except Exception:
                    # Should we allow parsing to continue in permissive mode?
                    # If so, what coordinates should we default to?  Easier to abort!
                    raise PDBConstructionException("Invalid or missing coordinate(s) at line %i."
                                                   % global_line_counter)
                coord = numpy.array((x, y, z), "f")
                # occupancy & B factor
                try:
                    occupancy = float(line[54:60])
                except Exception:
                    self._handle_PDB_exception("Invalid or missing occupancy",
                                               global_line_counter)
                    occupancy = None  # Rather than arbitrary zero or one
                if occupancy is not None and occupancy < 0:
                    # TODO - Should this be an error in strict mode?
                    # self._handle_PDB_exception("Negative occupancy",
                    #                            global_line_counter)
                    # This uses fixed text so the warning occurs once only:
                    warnings.warn("Negative occupancy in one or more atoms", PDBConstructionWarning)
                try:
                    bfactor = float(line[60:66])
                except Exception:
                    self._handle_PDB_exception("Invalid or missing B factor",
                                               global_line_counter)
                    bfactor = 0.0  # The PDB use a default of zero if the data is missing
                segid = line[72:76]
                element = line[76:78].strip().upper()
                if current_segid != segid:
                    current_segid = segid
                    structure_builder.init_seg(current_segid)
                if current_chain_id != chainid:
                    current_chain_id = chainid
                    structure_builder.init_chain(current_chain_id)
                    current_residue_id = residue_id
                    current_resname = resname
                    try:
                        structure_builder.init_residue(resname, hetero_flag, resseq, icode)
                    except PDBConstructionException as message:
                        self._handle_PDB_exception(message, global_line_counter)
                elif current_residue_id != residue_id or current_resname != resname:
                    current_residue_id = residue_id
                    current_resname = resname
                    try:
                        structure_builder.init_residue(resname, hetero_flag, resseq, icode)
                    except PDBConstructionException as message:
                        self._handle_PDB_exception(message, global_line_counter)
                # init atom
                try:
                    structure_builder.init_atom(name, coord, bfactor, occupancy, altloc,
                                                fullname, serial_number, element)
                except PDBConstructionException as message:
                    self._handle_PDB_exception(message, global_line_counter)
            elif record_type == "ANISOU":
                anisou = [float(x) for x in (line[28:35], line[35:42], line[43:49],
                                             line[49:56], line[56:63], line[63:70])]
                # U's are scaled by 10^4
                anisou_array = (numpy.array(anisou, "f") / 10000.0).astype("f")
                structure_builder.set_anisou(anisou_array)
            elif record_type == "MODEL ":
                try:
                    serial_num = int(line[10:14])
                except Exception:
                    self._handle_PDB_exception("Invalid or missing model serial number",
                                               global_line_counter)
                    serial_num = 0
                structure_builder.init_model(current_model_id, serial_num)
                current_model_id += 1
                model_open = 1
                current_chain_id = None
                current_residue_id = None
            elif record_type == "END   " or record_type == "CONECT":
                # End of atomic data, return the trailer
                self.line_counter += local_line_counter
                return coords_trailer[local_line_counter:]
            elif record_type == "ENDMDL":
                model_open = 0
                current_chain_id = None
                current_residue_id = None
            elif record_type == "SIGUIJ":
                # standard deviation of anisotropic B factor
                siguij = [float(x) for x in (line[28:35], line[35:42], line[42:49],
                                             line[49:56], line[56:63], line[63:70])]
                # U sigma's are scaled by 10^4
                siguij_array = (numpy.array(siguij, "f") / 10000.0).astype("f")
                structure_builder.set_siguij(siguij_array)
            elif record_type == "SIGATM":
                # standard deviation of atomic positions
                sigatm = [float(x) for x in (line[30:38], line[38:45], line[46:54],
                                             line[54:60], line[60:66])]
                sigatm_array = numpy.array(sigatm, "f")
                structure_builder.set_sigatm(sigatm_array)
            local_line_counter += 1
        # EOF (does not end in END or CONECT)
        self.line_counter = self.line_counter + local_line_counter
        return []

    def _handle_PDB_exception(self, message, line_counter):
        """Handle exception (PRIVATE).

        This method catches an exception that occurs in the StructureBuilder
        object (if PERMISSIVE), or raises it again, this time adding the
        PDB line number to the error message.
        """
        message = "%s at line %i." % (message, line_counter)
        if self.PERMISSIVE:
            # just print a warning - some residues/atoms may be missing
            warnings.warn("PDBConstructionException: %s\n"
                          "Exception ignored.\n"
                          "Some atoms or residues may be missing in the data structure."
                          % message, PDBConstructionWarning)
        else:
            # exceptions are fatal - raise again with new message (including line nr)
            raise PDBConstructionException(message)
Beispiel #24
0
def read_PIC(file):
    """Load Protein Internal Coordinate (PIC) data from file.

    PIC file format:
        # comment lines start with #
        (optional) PDB HEADER record
            - idcode and deposition date recommended but optional
            - deposition date in PDB format or as changed by Biopython
        (optional) PDB TITLE record
        repeat:
            Biopython Residue Full ID - sets ID of returned structure
            (optional) PDB ATOM records for chain start N, CA, C
            PIC Hedra records for residue
            PIC Dihedra records for residue

    :param Bio.File file: file name or handle
    :returns: Biopython Structure object, Residues with .pic attributes
        but no coordinates except for chain start N, CA, C atoms if supplied,
        or None on parse fail (silent, no exception rasied)
    """
    pdb_hdr_re = re.compile(
        r'^HEADER\s{4}(?P<cf>.{1,40})'
        r'(?:\s+(?P<dd>\d\d\d\d-\d\d-\d\d|\d\d-\w\w\w-\d\d))?'
        r'(?:\s+(?P<id>[0-9A-Z]{4}))?\s*$', )
    # ^\('(?P<pid>\w*)',\s(?P<mdl>\d+),\s'(?P<chn>\w)',\s\('(?P<het>\s|[\w-]+)',\s(?P<pos>\d+),\s'(?P<icode>\s|\w)'\)\)\s(?P<res>[A-Z]{3})\s(\[(?P<segid>[a-zA-z\s]{4})\])?\s*$
    pdb_ttl_re = re.compile(r'^TITLE\s{5}(?P<ttl>.+)\s*$')
    biop_id_re = re.compile(r"^\('(?P<pid>\w*)',\s(?P<mdl>\d+),\s"
                            r"'(?P<chn>\s|\w)',\s\('(?P<het>\s|[\w\s-]+)"
                            r"',\s(?P<pos>-?\d+),\s'(?P<icode>\s|\w)'\)\)"
                            r'\s+(?P<res>[\w]{1,3})'
                            r'(\s\[(?P<segid>[a-zA-z\s]+)\])?'
                            r'\s*$')
    pdb_atm_re = re.compile(r'^ATOM\s\s(?:\s*(?P<ser>\d+))\s(?P<atm>[\w\s]{4})'
                            r'(?P<alc>\w|\s)(?P<res>[\w]{3})\s(?P<chn>.)'
                            r'(?P<pos>[\s\-\d]{4})(?P<icode>[A-Za-z\s])\s\s\s'
                            r'(?P<x>[\s\-\d\.]{8})(?P<y>[\s\-\d\.]{8})'
                            r'(?P<z>[\s\-\d\.]{8})(?P<occ>[\s\d\.]{6})'
                            r'(?P<tfac>[\s\d\.]{6})\s{6}'
                            r'(?P<segid>[a-zA-z\s]{4})(?P<elm>.{2})'
                            r'(?P<chg>.{2})?\s*$')
    bfac_re = re.compile(r'^BFAC:\s([^\s]+\s+[\-\d\.]+)'
                         r'\s*([^\s]+\s+[\-\d\.]+)?'
                         r'\s*([^\s]+\s+[\-\d\.]+)?'
                         r'\s*([^\s]+\s+[\-\d\.]+)?'
                         r'\s*([^\s]+\s+[\-\d\.]+)?')
    bfac2_re = re.compile(r'([^\s]+)\s+([\-\d\.]+)')
    struct_builder = StructureBuilder()

    # init empty header dict
    # - could use to parse HEADER and TITLE lines except
    #   deposition_date format changed from original PDB header
    header_dict = _parse_pdb_header_list([])

    curr_SMCS = [None, None, None, None]  # struct model chain seg
    SMCS_init = [
        struct_builder.init_structure, struct_builder.init_model,
        struct_builder.init_chain, struct_builder.init_seg
    ]

    sb_res = None

    with as_handle(file, mode='r') as handle:
        for aline in handle.readlines():
            if aline.startswith('#'):
                pass  # skip comment lines
            elif aline.startswith('HEADER '):
                m = pdb_hdr_re.match(aline)
                if m:
                    header_dict['head'] = m.group('cf')  # classification
                    header_dict['idcode'] = m.group('id')
                    header_dict['deposition_date'] = m.group('dd')
                else:
                    print('Reading pic file', file, 'HEADER fail: ', aline)
                pass
            elif aline.startswith('TITLE '):
                m = pdb_ttl_re.match(aline)
                if m:
                    header_dict['name'] = m.group('ttl').strip()
                    # print('TTL: ', m.group('ttl').strip())
                else:
                    print('Reading pic file', file, 'TITLE fail:, ', aline)
            elif aline.startswith('('):  # Biopython ID line for Residue
                m = biop_id_re.match(aline)
                if m:
                    # check SMCS = Structure, Model, Chain, SegID
                    segid = m.group(9)
                    if segid is None:
                        segid = '    '
                    this_SMCS = [
                        m.group(1),
                        int(m.group(2)),
                        m.group(3), segid
                    ]
                    if curr_SMCS != this_SMCS:
                        # init new SMCS level as needed
                        for i in range(4):
                            if curr_SMCS[i] != this_SMCS[i]:
                                SMCS_init[i](this_SMCS[i])
                                curr_SMCS[i] = this_SMCS[i]
                                if 0 == i:
                                    # 0 = init structure so add header
                                    struct_builder.set_header(header_dict)
                                elif 1 == i:
                                    # new model means new chain and new segid
                                    curr_SMCS[2] = curr_SMCS[3] = None

                    struct_builder.init_residue(m.group('res'), m.group('het'),
                                                int(m.group('pos')),
                                                m.group('icode'))

                    sb_res = struct_builder.residue
                    if 2 == sb_res.is_disordered():
                        for r in sb_res.child_dict.values():
                            if not hasattr(r, 'internal_coord'):
                                sb_res = r
                                break
                    sb_res.internal_coord = IC_Residue(sb_res)
                    # print('res id:', m.groupdict())
                    # print(report_PIC(struct_builder.get_structure()))
                else:
                    print('Reading pic file', file, 'residue fail: ', aline)
            elif aline.startswith('ATOM '):
                m = pdb_atm_re.match(aline)
                if m:
                    if sb_res is None:
                        # ATOM without res spec already loaded, not a pic file
                        print('no sb_res - not pic file', aline)
                        return None
                    if (sb_res.resname != m.group('res')
                            or sb_res.id[1] != int(m.group('pos'))):
                        # TODO: better exception here?
                        raise Exception(
                            'pic ATOM read confusion: %s %s %s' %
                            (sb_res.resname, str(sb_res.id), aline))

                    coord = numpy.array(
                        (float(m.group('x')), float(
                            m.group('y')), float(m.group('z'))), "f")
                    struct_builder.init_atom(
                        m.group('atm').strip(), coord, float(m.group('tfac')),
                        float(m.group('occ')), m.group('alc'), m.group('atm'),
                        int(m.group('ser')),
                        m.group('elm').strip())

                    # print('atom: ', m.groupdict())
                else:
                    print('Reading pic file', file, 'ATOM fail: ', aline)
            elif aline.startswith('BFAC: '):
                m = bfac_re.match(aline)
                if m:
                    for bfac_pair in m.groups():
                        if bfac_pair is not None:
                            m2 = bfac2_re.match(bfac_pair)
                            if (m2 and sb_res is not None
                                    and hasattr(sb_res, 'internal_coord')):
                                rp = sb_res.internal_coord
                                rp.bfactors[m2.group(1)] = float(m2.group(2))
            else:
                m = Edron.edron_re.match(aline)
                if m:
                    sb_res.internal_coord.load_PIC(m.groupdict())
                elif aline.strip():
                    print('Reading PIC file', file, 'parse fail on: .', aline,
                          '.')
                    return None

    struct = struct_builder.get_structure()
    for chn in struct.get_chains():
        chnp = chn.internal_coord = IC_Chain(chn)
        # done in IC_Chain init : chnp.set_residues()
        chnp.link_residues()
        chnp.render_dihedra()

    # print(report_PIC(struct_builder.get_structure()))
    return struct