Beispiel #1
0
    def setResValues( self, model, values, key='temperature_factor',
                      lastOnly=0 ):
        """
        Add numeric value per residue to all atoms of all Structures
        or the last Structure in a model. The values will be written to
        either the B- (temperature_factor) or Q-factor 'occupancy' column
        in the temporary pdb-file.
        These values can then be used to display properties in PyMol
        via commands like 'color_b' and 'color_q'. See also
        L{setAtomValues}.
        
        @param model: model name
        @type  model: str
        @param values: list of numbers, len( values ) == number of residues
        @type  values: [float]      
        @param key: key for Atom.properties dictionary
                    (default: temperature_factor)
        @type  key: occupancy|temperature_factor
        @param lastOnly: 0 - add to all in model OR
                         1 - add only to last Structure (default: 0)
        @type  lastOnly: 1|0
        """
        if lastOnly:
            self.dic[ model ][-1].addResProperty( values, key )

        else:
            for m in self.dic[ model ]:
                try:
                    m.addResProperty( values, key )
                except:
                    T.errWriteln( "Warning: error while adding properties.")
                    T.errWriteln( "Key: "+str( key )+" values: "+str( values ) )
                    T.errWriteln( T.lastError() )
Beispiel #2
0
    def array_or_list( self, prof, asarray ):
        """
        Convert to array or list depending on asarray option
        
        Beware: empty lists will be upgraded to empty Float arrays.

        :param prof: profile
        :type  prof: list OR array
        :param asarray: 1.. autodetect type, 0.. force list, 2.. force array
        :type  asarray: 2|1|0
        
        :return: profile
        :rtype: list OR array
        
        :raise ProfileError:
        """
        try:

            ## autodetect type
            if asarray == 1:

                if isinstance( prof, N.ndarray ):
                    return self.__picklesave_array( prof )

                if type( prof ) is str:  # tolerate strings as profiles
                    return list( prof )
    
                if len(prof) == 0: # don't create arrays from empty lists
                    return list( prof )
                
                p = self.__picklesave_array( N.array( prof ) )
                if p.dtype.char not in ['O','c','S', 'U']: ## no char or object arrays!
                    return p

                return list( prof )

            ## force list
            if asarray == 0:

                if isinstance( prof, N.ndarray ):
                    return prof.tolist()

                return list( prof )

            ## force array
            if asarray == 2:
                if isinstance( prof, N.ndarray ):
                    return self.__picklesave_array( prof )
                
                return self.__picklesave_array( N.array( prof ) )

        except TypeError as why:
            ## Numeric bug: N.array(['','','']) raises TypeError
            if asarray == 1 or asarray == 0:
                return list( prof )

            raise ProfileError("Cannot create array from given list. %r"\
                  % T.lastError())

        raise ProfileError("%r not allowed as value for asarray" % asarray)
Beispiel #3
0
    def add(self, str):
        """
        Add String str and line break to file.

        @param str: string to add to pml file
        @type  str: str        
        """
        try:
            self.fgenerate.write(str + '\n')
        except (IOError):
            T.errWriteln(
                "PymolInput.add(): Error adding string to pymol script file.")
            T.errWriteln( T.lastError() )
Beispiel #4
0
    def failed( self ):
        """
        If HEX job fails
        """
        print("FAILED: ", self.host, ' ', t.stripFilename(self.finp))
        print("\tJob details:")
        print("\tCommand: ", self.cmd)
        print("\tinput:   ", self.finp)
        print("\tHex log: ", self.log)
        print("\tHex out: ", self.fout)
        print()
        print("\t", t.lastError())

        self.owner.failedHex( self )
Beispiel #5
0
    def failed(self):
        """
        If HEX job fails
        """
        print("FAILED: ", self.host, ' ', t.stripFilename(self.finp))
        print("\tJob details:")
        print("\tCommand: ", self.cmd)
        print("\tinput:   ", self.finp)
        print("\tHex log: ", self.log)
        print("\tHex out: ", self.fout)
        print()
        print("\t", t.lastError())

        self.owner.failedHex(self)
Beispiel #6
0
    def fatal( self, message ):
        """
        Handle a fatal error (likely a bug), stop program execution.

        :param message: message to be given to user
        :type  message: str

        :raise FatalError: 
        """
        s = '\nFatal Error: '+str(message)
        s += '\n\t' + T.lastError() + '\n'
        s += 'TraceBack: \n' + T.lastErrorTrace() + '\n'

        self.log.add(s)
        raise FatalError
Beispiel #7
0
    def error( self, message ):
        """
        Handle a normal error (like non-existing file) that is not
        necessarily a bug.

        :param message: message to be given to user
        :type  message: str

        :raise NormalError: 
        """
        s = '\nError: '+str(message)
        s += '\n\t' + T.lastError()
        s += '\nTraceBack: \n' + T.lastErrorTrace() + '\n'

        self.log.add(s)
        raise NormalError
Beispiel #8
0
    def remove_multi_occupancies( self ):
        """
        Keep only atoms with alternate A field (well, or no alternate).
        """
        if self.verbose:
            self.logWrite( self.model.pdbCode +
                           ': Removing multiple occupancies of atoms ...')

        i = 0
        to_be_removed = []

        for a in self.model:

            if a['alternate']:
                try:
                    str_id = "%i %s %s %i" % (a['serial_number'], a['name'],
                                              a['residue_name'],
                                              a['residue_number'])

                    if a['alternate'].upper() in ['A', '1']:
                        a['alternate'] = ''

                    else:
                        if float( a['occupancy'] ) < 1.0:
                            to_be_removed += [ i ]
                            if self.verbose:
                                self.logWrite(
                                    'removing %s (%s %s)' %
                                    (str_id,a['alternate'], a['occupancy']))
                        else:
                            if self.verbose:
                                self.logWrite(
                                 ('keeping non-A duplicate %s because of 1.0 '+
                                  'occupancy') % str_id )

                except:
                    self.logWrite("Error removing duplicate: "+t.lastError() )
            i+=1

        try:
            self.model.remove( to_be_removed )
            if self.verbose:
                self.logWrite('Removed %i atoms' % len( to_be_removed ) )

        except:
            if self.verbose:
                self.logWrite('No atoms with multiple occupancies to remove' )
Beispiel #9
0
    def remove_multi_occupancies(self):
        """
        Keep only atoms with alternate A field (well, or no alternate).
        """
        if self.verbose:
            self.logWrite(self.model.pdbCode +
                          ': Removing multiple occupancies of atoms ...')

        i = 0
        to_be_removed = []

        for a in self.model:

            if a['alternate']:
                try:
                    str_id = "%i %s %s %i" % (a['serial_number'], a['name'],
                                              a['residue_name'],
                                              a['residue_number'])

                    if a['alternate'].upper() in ['A', '1']:
                        a['alternate'] = ''

                    else:
                        if float(a['occupancy']) < 1.0:
                            to_be_removed += [i]
                            if self.verbose:
                                self.logWrite(
                                    'removing %s (%s %s)' %
                                    (str_id, a['alternate'], a['occupancy']))
                        else:
                            if self.verbose:
                                self.logWrite((
                                    'keeping non-A duplicate %s because of 1.0 '
                                    + 'occupancy') % str_id)

                except:
                    self.logWrite("Error removing duplicate: " + t.lastError())
            i += 1

        try:
            self.model.remove(to_be_removed)
            if self.verbose:
                self.logWrite('Removed %i atoms' % len(to_be_removed))

        except:
            if self.verbose:
                self.logWrite('No atoms with multiple occupancies to remove')
Beispiel #10
0
    def warning( self, message, error=1, trace=0 ):
        """
        Issue a warning. No exception is raised.

        :param message: message to be given to user
        :type  message: str
        :param error: report Exception with line (default: 1)
        :type  error: 1||0
        :param trace: report full back trace to exception (default: 0)
        :type  trace: 1||0
        """
        s = '\nWarning (ignored): '+str(message)
        try:
            if trace: error = 1
            if error:
                s += '\n\t' + T.lastError() + '\n'
            if trace:
                s += '\nTraceBack: \n' + T.lastErrorTrace() + '\n'
        except:
            pass

        self.log.add(s)
Beispiel #11
0
    def addResProperty( self, values, key='temperature_factor'):
        """
        Does the same thing as L{addProperty} but on the residue level,
        i.e adds extra value to each residue in Structure.
        (The same value is added to all atoms of a residue.)
        These values can then be used to display properties in PyMol
        via commands like 'color_b' and 'color_q'.
        
        @param values: list of numbers, len( values ) == number of residues
        @type  values: [float]      
        @param key: key for Atom.properties dictionary
                    ('occupancy' OR 'temperature_factor')
        @type  key: occupancy|temperature_factor
        """
        try:
            if self.struct is None:
                self.struct = PDBModel( self.fname )
                self.temporary = 1

            self.struct[ key ] = self.struct.res2atomProfile( values )
        except:
            print(T.lastError())
Beispiel #12
0
    def generateInp(self):
        """
        Prepare the program input (file or string) from a template (if
        any, file or string).

        :return: input file name OR (if pipes=1) content of input file OR None
        :rtype: str
        
        :raise TemplateError: if error while creating template file
        """
        try:
            inp = None

            if self.template:
                inp = self.fillTemplate()

            return self.convertInput(inp)

        except IOError as why:
            s = "Error while creating input file from template."
            s += "\n  template file: " + str(self.template)
            s += "\n  why: " + str(why)
            s += "\n  Error:\n  " + t.lastError()
            raise TemplateError(s)
Beispiel #13
0
    def generateInp(self):
        """
        Prepare the program input (file or string) from a template (if
        any, file or string).

        :return: input file name OR (if pipes=1) content of input file OR None
        :rtype: str
        
        :raise TemplateError: if error while creating template file
        """
        try:
            inp = None

            if self.template:
                inp = self.fillTemplate()

            return self.convertInput( inp )

        except IOError as why:
            s =  "Error while creating input file from template."
            s += "\n  template file: " + str( self.template )
            s += "\n  why: " + str( why )
            s += "\n  Error:\n  " + t.lastError()
            raise TemplateError(s)
Beispiel #14
0
    def update(self,
               model,
               source,
               skipRes=None,
               updateMissing=0,
               force=0,
               headPatterns=[]):
        """
        Update empty or missing fields of model from the source. The
        model will be connected to the source via model.source.
        Profiles that are derived from the source are labeled 'changed'=0.
        The same holds for coordinates (xyzChanged=0).
        However, existing profiles or coordinates or fields remain untouched.

        :param model: existing model
        :type  model: PDBModel
        :param source: source PDB file
        :type  source: str
        :param skipRes: list residue names that should not be parsed
        :type  skipRes: [ str ]
        :param updateMissing: ignored
        :type  updateMissing: 1|0
        :param headPatterns: [(putIntoKey, regex)] extract given REMARKS
        :type  headPatterns: [(str, str)]

        :raise PDBParserError - if something is wrong with the source file
        """

        try:
            ## atoms and/or coordinates need to be updated from PDB
            if force or self.needsUpdate(model):

                atoms, xyz, info = self.__collectAll(source, skipRes,
                                                     headPatterns)

                keys = M.union(list(atoms.keys()), list(self.DEFAULTS.keys()))

                for k in keys:

                    a = model.atoms.get(k, default=0, update=False)
                    if (a is 0) or (a is None):

                        dflt = self.DEFAULTS.get(k, None)
                        model.atoms.set(k, atoms.get(k, dflt), changed=0)

                if model.xyz is None:
                    model.xyz = xyz
                    model.xyzChanged = 0

                model._resIndex = None
                model._chainIndex = None

                model.fileName = model.fileName or source

                model.pdbCode = model.pdbCode or info.get('pdb_code', None) or \
                                self.idFromName( model.fileName)

                ## ## make biounit from the dictionary we have parsed                              if 'BIOMT' in info:
                ##     biomt = info['BIOMT']
                ##     model.biounit = BU.BioUnit(model, biomt)
                ##     del info['BIOMT']

                model.info.update(info)

        except:
            msg = self.__xplorAtomIndicesTest(source) or ' '
            raise PDBParserError('Cannot read ' + str(source) + ' as PDB\n'\
                           '\ERROR: ' + T.lastError() + msg)

        model.setSource(source)
Beispiel #15
0
    def array_or_list(self, prof, asarray):
        """
        Convert to array or list depending on asarray option
        
        Beware: empty lists will be upgraded to empty Float arrays.

        :param prof: profile
        :type  prof: list OR array
        :param asarray: 1.. autodetect type, 0.. force list, 2.. force array
        :type  asarray: 2|1|0
        
        :return: profile
        :rtype: list OR array
        
        :raise ProfileError:
        """
        try:

            ## autodetect type
            if asarray == 1:

                if isinstance(prof, N.ndarray):
                    return self.__picklesave_array(prof)

                if type(prof) is str:  # tolerate strings as profiles
                    return list(prof)

                if len(prof) == 0:  # don't create arrays from empty lists
                    return list(prof)

                p = self.__picklesave_array(N.array(prof))
                if p.dtype.char not in ['O', 'c', 'S',
                                        'U']:  ## no char or object arrays!
                    return p

                return list(prof)

            ## force list
            if asarray == 0:

                if isinstance(prof, N.ndarray):
                    return prof.tolist()

                return list(prof)

            ## force array
            if asarray == 2:
                if isinstance(prof, N.ndarray):
                    return self.__picklesave_array(prof)

                return self.__picklesave_array(N.array(prof))

        except TypeError as why:
            ## Numeric bug: N.array(['','','']) raises TypeError
            if asarray == 1 or asarray == 0:
                return list(prof)

            raise ProfileError("Cannot create array from given list. %r"\
                  % T.lastError())

        raise ProfileError("%r not allowed as value for asarray" % asarray)
Beispiel #16
0
    def __collectAll(self, fname, skipRes=None, headPatterns=[]):
        """
        Parse ATOM/HETATM lines from PDB. Collect coordinates plus
        dictionaries with the other pdb records of each atom.
        REMARK, HEADER, etc. lines are ignored.

        Some changes are made to the dictionary from PDBFile.readline()::
            - the 'position' entry (with the coordinates) is removed
            - leading and trailing spaces are removed from 'name' ..
            - .. but a 'name_original' entry keeps the old name with spaces
            - a 'type' entry is added. Its value is 'ATOM' or 'HETATM'
            - a 'after_ter' entry is added. Its value is 1, if atom is
              preceeded by a 'TER' line, otherwise 0
            - empty 'element' entries are filled with the first non-number
              letter from the atom 'name'

        :param fname: name of pdb file
        :type  fname: str
        :param skipRes: list with residue names that should be skipped
        :type  skipRes: list of str

        :return: tuple of (1) dictionary of profiles
                 and (2) xyz array N x 3
        :rtype: ( list, array )
        """
        xyz = []

        aProfs = {}

        info = {}

        in_header = True

        headPatterns = headPatterns or self.RE_REMARKS
        patterns = [(key, re.compile(ex)) for key, ex in headPatterns]

        for k in B.PDBModel.PDB_KEYS:
            aProfs[k] = list()

        f = IO.PDBFile(fname)

        skipLine = False

        try:
            line, i = ('', ''), 0

            while line[0] != 'END' and line[0] != 'ENDMDL':

                i += 1
                if not skipLine:
                    try:
                        line = f.readLine()
                    except ValueError as what:
                        self.log.add('Warning: Error parsing line %i of %s' %
                                     (i, T.stripFilename(fname)))
                        self.log.add('\tError: ' + str(what))
                        continue
                else:
                    skipLine = False

                ## header handling
                if in_header and line[0] == 'HEADER':
                    info.update(self.__parseHeader(line))

                if in_header and line[0] == 'REMARK':
                    if line[1].startswith(' 350'):
                        biomtDict, line = self.__parseBiomt(f, line)
                        info.update(biomtDict)
                        # we've hogged a line beyond REMARK 350 records in
                        # __parseBiomt(), now we need to process it here
                        skipLine = True
                        continue
                    else:
                        info.update(self.__parseRemark(line, patterns))

                ## preserve position of TER records
                newChain = line[0] == 'TER'
                if newChain:
                    line = f.readLine()

                if (line[0] in ['ATOM', 'HETATM']):

                    if in_header:
                        in_header = False  ## switch off HEADER parsing

                    a = line[1]

                    if skipRes and a['residue_name'] in skipRes:
                        continue

                    a['name_original'] = a['name']
                    a['name'] = a['name'].strip()

                    a['type'] = line[0]

                    if newChain:
                        a['after_ter'] = 1
                    else:
                        a['after_ter'] = 0

                    if a['element'] == '':
                        a['element'] = self.__firstLetter(a['name'])

                    xyz.append(a['position'])

                    del a['position']

                    for k, v in a.items():
                        aProfs[k].append(v)

        except:
            raise PDBParserError("Error parsing file "+fname+": " \
                                 + T.lastError())
        try:
            f.close()
        except:
            pass

        if len(xyz) == 0:
            raise PDBParserError("Error parsing file " + fname + ": " +
                                 "Couldn't find any atoms.")

        return aProfs, N0.array(xyz, N0.Float32), info
Beispiel #17
0
    def update( self, model, source, skipRes=None, updateMissing=0, force=0,
                headPatterns=[]):
        """
        Update empty or missing fields of model from the source. The
        model will be connected to the source via model.source.
        Profiles that are derived from the source are labeled 'changed'=0.
        The same holds for coordinates (xyzChanged=0).
        However, existing profiles or coordinates or fields remain untouched.

        :param model: existing model
        :type  model: PDBModel
        :param source: source PDB file
        :type  source: str
        :param skipRes: list residue names that should not be parsed
        :type  skipRes: [ str ]
        :param updateMissing: ignored
        :type  updateMissing: 1|0
        :param headPatterns: [(putIntoKey, regex)] extract given REMARKS
        :type  headPatterns: [(str, str)]

        :raise PDBParserError - if something is wrong with the source file
        """

        try:
            ## atoms and/or coordinates need to be updated from PDB
            if force or self.needsUpdate( model ):
    
                atoms, xyz, info = self.__collectAll( source, skipRes, 
                                                      headPatterns )
    
                keys = M.union( list(atoms.keys()),  list(self.DEFAULTS.keys()) )
    
                for k in keys:
    
                    a = model.atoms.get( k, default=0, update=False )
                    if (a is 0) or (a is None):
                    
                        dflt = self.DEFAULTS.get( k, None )
                        model.atoms.set(k, atoms.get(k, dflt), changed=0 )
    
                if model.xyz is None:
                    model.xyz = xyz
                    model.xyzChanged = 0
    
                model._resIndex  =None
                model._chainIndex=None
    
                model.fileName = model.fileName or source
    
                model.pdbCode = model.pdbCode or info.get('pdb_code', None) or \
                                self.idFromName( model.fileName)
                
                ## ## make biounit from the dictionary we have parsed                              if 'BIOMT' in info:
                ##     biomt = info['BIOMT']
                ##     model.biounit = BU.BioUnit(model, biomt)
                ##     del info['BIOMT']
                
                model.info.update( info )
                           
        except:
            msg = self.__xplorAtomIndicesTest( source ) or ' '
            raise PDBParserError('Cannot read ' + str(source) + ' as PDB\n'\
                           '\ERROR: ' + T.lastError() + msg)

        model.setSource( source )
Beispiel #18
0
    def nextComplex(self):
        """
        Take list of lines, extract all Hex info about one complex
        (Solution number, hex energy,..) also extract 16 numbers of
        the transformation matrix and put them into 4 by 4 numeric
        array.

        @return: Complex created from the output from Hex
        @rtype: Complex
        """
        ## get set of lines describing next complex:
        lines = self._nextBlock()
        if lines is None:
            ## EOF
            return None
        ## skip incomplete records
        if len(lines) < 13:
            lines = self._nextBlock()

        ## fill info dictionary
        i = {}
        matrix = None
        for l in lines:
            try:
                ## labels has to be in the same order as in hex.out
                m = self.ex_line.search(l)
                if m is not None:
                    m = m.groups()

                    if m[0] == 'Orientation':
                        i['hex_clst'] = int(m[1])
                    elif m[0] == 'Solution':
                        i['soln'] = int(m[1])
                    elif m[0] == 'ReceptorModel':
                        if self.forceModel:
                            i['model1'] = self.forceModel[0]
                        else:
                            i['model1'] = int(m[1])
                    elif m[0] == 'LigandModel':
                        if self.forceModel:
                            i['model2'] = self.forceModel[1]
                        else:
                            i['model2'] = int(m[1])
                    elif m[0] == 'Bumps':
                        if int(m[1]) != -1:
                            i['bumps'] = int(m[1])
                    elif m[0] == 'ReferenceRMS':
                        i['rms'] = float(m[1])
                    elif m[0] == 'Vshape':
                        if float(m[1]) != 0.0:
                            i['hex_Vshape'] = float(m[1])
                    elif m[0] == 'Vclash':
                        if float(m[1]) != 0.0:
                            i['hex_Vclash'] = float(m[1])
                    elif m[0] == 'Etotal':
                        i['hex_etotal'] = float(m[1])
                    elif m[0] == 'Eshape':
                        i['hex_eshape'] = float(m[1])
                    elif m[0] == 'Eair':
                        i['hex_eair'] = float(m[1])
                    elif m[0] == 'LigandMatrix':
                        ## get all numbers of matrix as list of strings
                        strings = self.ex_matrix.findall(l)
                        ## convert that to list of floats
                        numbers = []
                        for each in strings:
                            numbers += [float(each)]
                        ## convert that to list of lists of 4 floats each
                        matrix = []
                        for j in range(0, 4):
                            matrix.append(numbers[4 * j:4 * (j + 1)])
                        ## create 4 by 4 Numeric array from 4 by 4 list
                        matrix = N0.array(matrix, N0.Float32)
            except AttributeError:
                print("HexParser.nextComplex(): ", t.lastError())

        ## Create new complex taking PCR models from dictionary
        c = Complex(self.rec_models[i['model1']], self.lig_models[i['model2']],
                    matrix, i)
        return c
Beispiel #19
0
    def __collectAll( self, fname, skipRes=None, headPatterns=[] ):
        """
        Parse ATOM/HETATM lines from PDB. Collect coordinates plus
        dictionaries with the other pdb records of each atom.
        REMARK, HEADER, etc. lines are ignored.

        Some changes are made to the dictionary from PDBFile.readline()::
            - the 'position' entry (with the coordinates) is removed
            - leading and trailing spaces are removed from 'name' ..
            - .. but a 'name_original' entry keeps the old name with spaces
            - a 'type' entry is added. Its value is 'ATOM' or 'HETATM'
            - a 'after_ter' entry is added. Its value is 1, if atom is
              preceeded by a 'TER' line, otherwise 0
            - empty 'element' entries are filled with the first non-number
              letter from the atom 'name'

        :param fname: name of pdb file
        :type  fname: str
        :param skipRes: list with residue names that should be skipped
        :type  skipRes: list of str

        :return: tuple of (1) dictionary of profiles
                 and (2) xyz array N x 3
        :rtype: ( list, array )
        """
        xyz   = []

        aProfs = {}

        info = {}

        in_header = True
        
        headPatterns = headPatterns or self.RE_REMARKS
        patterns = [ (key, re.compile(ex)) for key,ex in headPatterns ]
        
        for k in B.PDBModel.PDB_KEYS:
            aProfs[k] = list()

        f = IO.PDBFile( fname )

        skipLine = False

        try:
            line, i = ('',''), 0

            while line[0] != 'END' and line[0] != 'ENDMDL':

                i += 1
                if not skipLine:
                    try:
                        line = f.readLine()
                    except ValueError as what:
                        self.log.add('Warning: Error parsing line %i of %s' %
                                     (i, T.stripFilename( fname )) )
                        self.log.add('\tError: '+str(what) )
                        continue
                else:
                    skipLine = False

                ## header handling
                if in_header and line[0] == 'HEADER':
                    info.update( self.__parseHeader( line ) )

                if in_header and line[0] == 'REMARK':
                    if line[1].startswith(' 350'):
                        biomtDict, line = self.__parseBiomt( f, line )
                        info.update( biomtDict )
                        # we've hogged a line beyond REMARK 350 records in 
                        # __parseBiomt(), now we need to process it here
                        skipLine = True
                        continue
                    else:
                        info.update( self.__parseRemark( line, patterns ) )
                    

                ## preserve position of TER records
                newChain = line[0] == 'TER'
                if newChain:
                    line = f.readLine()

                if (line[0] in ['ATOM','HETATM'] ):

                    if in_header: in_header = False  ## switch off HEADER parsing
                    
                    a = line[1]

                    if skipRes and a['residue_name'] in skipRes:
                        continue

                    a['name_original'] = a['name']
                    a['name'] = a['name'].strip()

                    a['type'] = line[0]

                    if newChain:
                        a['after_ter'] = 1
                    else:
                        a['after_ter'] = 0

                    if a['element'] == '':
                        a['element'] = self.__firstLetter( a['name'] )

                    xyz.append( a['position'] )

                    del a['position']

                    for k, v in a.items():
                        aProfs[k].append( v )

        except:
            raise PDBParserError("Error parsing file "+fname+": " \
                                 + T.lastError())
        try:
            f.close()
        except:
            pass

        if len( xyz ) == 0:
            raise PDBParserError("Error parsing file "+fname+": "+
                            "Couldn't find any atoms.")

        return aProfs, N0.array( xyz, N0.Float32 ), info