Пример #1
0
def compare( project1, project2 ):
    """Compare the shifts of all equivalent atoms.
    """
    if project1 is None or project1.molecule is None:
        return True
    if project2 is None or project2.molecule is None:
        return True

    mol1 = project1.molecule
    mol2 = project2.molecule

    nTmessage('==> comparing %s and %s\n', mol1, mol2)

    mapMolecule(mol1, mol2)

    for atm1 in mol1.allAtoms():
        if atm1.has_key('delta'): del(atm1['delta'])
    #end for
    for atm1 in mol1.allAtoms():
        compareAtms(atm1, atm1.map)
    #end for

    # average; taken the values of directly bonded atoms into account
    for atm in mol1.allAtoms():
        average(atm)
    #end for

    # generate scripts
    mkYasaraMacro(mol1, 'delta', sprintf('yasara-%s-%s.mcr', project1.name, project2.name))
    mkYasaraMacro(mol1, 'deltaAverage', sprintf('yasara-%s-%s-averaged.mcr', project1.name, project2.name))
Пример #2
0
def mkYasaraMacro(mol, attr, path):
    """Create a Yasara macro using attr
    """
    nTmessage('==> Creating macro %s\n', path)
    yasara = open(path, 'w')

    fprintf(yasara, 'Console off\n')
    fprintf(yasara, 'ColorAtom All,Gray\n')
    fprintf(yasara, 'PropAtom All,-999\n')

    for atm in mol.allAtoms():
        an = atm.translate('PDB')
        # tmp hack, convert to YASARA format since PDB HB3 does not work
        if an and an[0:1].isdigit():
            an = an[1:]
        #end if
#        print atm, an
        if an and atm.has_key(attr):
#            print '>',atm[attr]
            a = 1.0 - atm[attr]
            a = max( a, 0.0 )
            fprintf(yasara, 'PropAtom residue %d atom %s,%.2f\n', atm.residue.resNum, an, a)
        #end if
    #end for

    fprintf(yasara, 'Console off\n')
    fprintf(yasara, 'ColorAll Property\n')
    fprintf(yasara, 'Console on\n')
    yasara.close()
Пример #3
0
def setToSingleCoreOperation():
    'Set the cing attribute .ncpus to 1'
    if cing.ncpus > 1:
        nTmessage("Scaling back to single core operations.")
        cing.ncpus = 1
        return
    nTmessage("Maintaining single core operations.")
Пример #4
0
def restoreQueeny100( project, tmp=None ):
    """
    Restore queeny results from sml file.

    Return True on error
    """
    if project is None:
        nTmessage("restoreQueeny100: No project defined")
        return True

    if project.molecule is None:
        return False # Gracefully returns

    queenyDefs = project.getStatusDict(constants.QUEENY_KEY)

    if not queenyDefs.completed: # old definition
        nTdebug('restoreQueeny100: no queeny completed')
        return False # Return gracefully

    path = project.validationPath( 'Queeny')
    if not path.exists():
        ntu.nTwarning('restoreQueeny100: directory "%s" with prior queeny data not found', path)
    else:
        # delete the data and run again
        os.rename(path, project.path() / cdefs.directories.version1 / 'Queeny')
    #end if
    # regenerate the data
    project.runQueeny()
    nTmessage('restoreQueeny100: Re-generated Queeny results')
    return False
Пример #5
0
def setToSingleCoreOperation():
    'Set the cing attribute .ncpus to 1'
    if cing.ncpus > 1:
        nTmessage("Scaling back to single core operations.")
        cing.ncpus = 1
        return
    nTmessage("Maintaining single core operations.")
Пример #6
0
def calculatePairWiseRmsd( mol1, mol2, ranges=None ):
    """Calculate pairwise rmsd between mol1 and mol2
       Optionally use ranges for the fitting
    """

    #Use ranges routines to define fitAtoms ed
    fitResidues1 = mol1.setResiduesFromRanges(ranges)
    mol1.selectFitAtoms( fitResidues1, backboneOnly=True, includeProtons = False )
    fitResidues2 = mol2.setResiduesFromRanges(ranges)
    mol2.selectFitAtoms( fitResidues2, backboneOnly=True, includeProtons = False )
#    mol2.superpose( ranges )

    l1 = len(mol1.ensemble)
    l2 = len(mol2.ensemble)

    if (   l1 == 0 or len(mol1.ensemble[0].fitCoordinates) == 0
        or l2 == 0 or len(mol2.ensemble[0].fitCoordinates) == 0
        or len(mol1.ensemble[0].fitCoordinates) != len(mol2.ensemble[0].fitCoordinates)
    ):
        nTdebug( ">calculatePairWiseRmsd> returning None, %s %s %s" , l1, l2, ranges)
        return None, None, None, None


    models = mol1.ensemble + mol2.ensemble

    result = NTlistOfLists(len(models), len(models), 0.0)

    nTmessage('==> Calculating pairwise rmsds %s %s', mol1, mol2)

    for i in range(len(models)):
        for j in range(i+1, len(models)):
            result[i][j] = models[i].superpose( models[j] )
            result[j][i] = result[i][j]
        #end for
    #end for

    pairwise1 = NTlist()
    for i in range(l1):
        for j in range(i+1, l1):
            pairwise1.append(result[i][j])
#            print '1>', i,j

    pairwise2 = NTlist()
    for i in range(l1, l1+l2):
        for j in range(i+1, l1+l2):
            pairwise2.append(result[i][j])
#            print '2>', i,j

    pairwise12 = NTlist()
    for i in range(l1):
        for j in range(l1, l1+l2):
            pairwise12.append(result[i][j])

#            print '12>', i,j
#    print len(pairwise1), len(pairwise2), len(pairwise12)
    return ( result, pairwise1.average2(fmt='%6.2f +- %5.2f'),pairwise2.average2(fmt='%6.2f +- %5.2f'),
        pairwise12.average2(fmt='%6.2f +- %5.2f'))
Пример #7
0
def upgrade100(project, restore):
    """
    Do all things to upgrade project to current configuration
    All versions <= 1.00
    """
    nTmessage('*** upgrade100: upgrading %s from version %s ***', project, project.version)
    verbosity = cing.verbosity
    # make sure we get all if we heave debug on
    if cing.verbosity < cing.verbosityDebug:
        cing.verbosity = cing.verbosityWarning

    # Molecules
    for molName in project.moleculeNames:
        pathName = project.molecules.path(molName)
        mol = Molecule.open(pathName)
        if mol:
            mol.status = 'keep'
            project.appendMolecule(mol)
        #end if
        nTdebug('upgrade100: restored %s', mol)
    #end for

    # restore the lists
    for pl in [project.peaks, project.distances, project.dihedrals, project.rdcs, project.coplanars]:
        pl.restore()
    #end for

    # Now patch talos+
    nTmessage('==> upgrade100: talosPlus')
    if restoreTalosPlus100(project):
        io.error('upgrade100: restoring talosPlus data failed\n')

    # Now patch queeny
    # nTmessage('==> upgrade100: queeny')
    # if restoreQueeny100(project):
    #     nTerror('upgrade100: restoring queeny data failed')
    # project.saveQueeny()
    #
    # # Now patch shiftx
    # if restoreShiftx100(project):
    #     nTerror('upgrade100: restoring shiftx data failed')
    #     return None
    # project.saveShiftx()

    # Plugin registered functions
    nTdebug('upgrade100: calling plugins')
    project._callPluginRestores()

    # save to consolidate
    project.save()

    cing.verbosity = verbosity
    return Project.open(project.name, constants.PROJECT_OLD, restore=restore)
Пример #8
0
def parseShiftx(project, tmp=None):
    """
    Parse the output generated by the shiftx program
    """
    if project is None:
        nTmessage("parseShiftx: No project defined")
        return True

    if project.molecule is None:
        nTmessage("parseShiftx: No molecule defined")
        return True

    defs = project.getStatusDict(constants.SHIFTX_KEY, **shiftxStatus())

    if not defs.completed:
        nTmessage("parseShiftx: No shiftx was run")
        return True

    path = project.validationPath(defs.directory)
    if not path:
        nTerror('parseShiftx: directory "%s" with shiftx data not found', path)
        return True

    _resetShiftx(project)
    # print '>>', defs, len(defs.chains)
    for chainId, fname in defs.chains:
        if _parseShiftxOutput(path / fname, project, chainId):
            return True
    # end for
    defs.parsed = True
    _calculatePseudoAtomShifts(project, len(defs.models))
    _averageShiftx(project)
    calcQshift(project)
    return False
Пример #9
0
def talosPlus2restraints( project, name=constants.TALOSPLUS_LIST_STR, status='noRefine', errorFactor=2.0 ):
    """
    Convert talos+ results to a CING dihedral restraint list
    """
    if project == None:
        nTmessage("talosPlus2restraints: No project defined")
        return True

    if project.molecule == None:
        nTmessage("talosPlus2restraints: No project defined")
        return True

    if not project.status.has_key('talosPlus') or not project.status.talosPlus.completed:
        nTmessage("talosPlus2restraints: No talos+ data")
        return True

    if name in project.dihedrals.names():
        project.dihedrals.delete(name)

    dhl = project.dihedrals.new(name=name, status=status)
    for res in project.molecule.allResidues():
        if res.talosPlus and res.talosPlus.classification=='Good':
            lower = res.talosPlus.phi.value-errorFactor*res.talosPlus.phi.error
            upper = res.talosPlus.phi.value+errorFactor*res.talosPlus.phi.error
            atoms = getDeepByKeysOrAttributes( res, constants.PHI_STR, constants.ATOMS_STR )
            if atoms:
                d = DihedralRestraint(atoms, lower, upper)
                dhl.append(d)

            lower = res.talosPlus.psi.value-errorFactor*res.talosPlus.psi.error
            upper = res.talosPlus.psi.value+errorFactor*res.talosPlus.psi.error
            atoms = getDeepByKeysOrAttributes( res, constants.PSI_STR, constants.ATOMS_STR )
            if atoms:
                d = DihedralRestraint(atoms, lower, upper)
                dhl.append(d)
        #end if
    #end for
    nTmessage('==> Created %s', dhl)
Пример #10
0
def calcQshift(project):
    """Calculate per residue Q factors between assignment and shiftx results
    """
    if project is None:
        nTmessage("calcQshift: no project defined")
        return None
    # end if

    if not project.molecule:
        nTmessage("calcQshift: no molecule defined")
        return None
    # end if
    nTdetail("==> Calculating Q-factors for chemical shift")
    for res in project.molecule.allResidues():
        atms = res.allAtoms()
        bb = NTlist()
        heavy = NTlist()
        protons = NTlist()

        for a in atms:
            if a.isBackbone():
                bb.append(a)
            if a.isProton():
                protons.append(a)
            else:
                heavy.append(a)
        # end for

        result = project.validationData.getResult(res, constants.SHIFTX_KEY, QshiftxResult())
        if result is None:
            nTmessage("calcQshift: error setting QshiftResult for residue %s", res)
            return None
        # end if
        result[QshiftxResult.ALL_ATOMS] = _calcQshift(project, atms)
        result[QshiftxResult.BACKBONE] = _calcQshift(project, bb)
        result[QshiftxResult.HEAVY_ATOMS] = _calcQshift(project, heavy)
        result[QshiftxResult.PROTONS] = _calcQshift(project, protons)

        # LEGACY
        qshiftDict = legacyQshiftDict()
        for k in [QshiftxResult.ALL_ATOMS, QshiftxResult.BACKBONE, QshiftxResult.HEAVY_ATOMS, QshiftxResult.PROTONS]:
            qshiftDict[k] = result[k]
        qshiftDict["residue"] = res
        res.Qshift = qshiftDict
Пример #11
0
def _runQueeny( project, tmp=None ):
    """Perform a queeny analysis and save the results.

    Returns True on error.
    Returns False when all is fine.
    """
    nTmessage("==> Calculating restraint information by Queeny")
    if project is None:
        nTerror("runQueeny: No project defined")
        return True

    if project.molecule is None:
        nTerror("runQueeny: No molecule defined")
        return True

    if len(project.distances) == 0:
        nTmessage("==> runQueeny: No distance restraints defined.")
        return True

    queenyDefs = project.getStatusDict(constants.QUEENY_KEY, **queenyDefaults())
    queenyDefs.molecule = project.molecule.asPid

    path = project.validationPath( queenyDefs.directory )
    if not path:
        nTmessage("==> runQueeny: error creating '%s'", path)
        return True

    q = Queeny( project )
    q.execute()
    queenyDefs.date = io.now()
    queenyDefs.completed = True
    queenyDefs.parsed = True
    queenyDefs.version = __version__

    del(q)

    return False
Пример #12
0
    def initRestraints( self ):
        """
        Initialize restraints from restraint lists
        Only distances for now
        """

        nTmessage('==> Queeny adding restraints (# elements = %d)', len(self))

        for dme in self.itervalues():
            dme.upperChange = 0.0
        #end for

        nkeys = len(self)
        #print '>', nkeys
#        count = 0
        for drl in self.project.distances:
            for dr in drl:
                if len(dr.atomPairs) == 1:
                    atm1,atm2 = dr.atomPairs[0]
                    upper = dr.upper
                    if dr.upper is None: # sometimes happens; i.e. entry 1but
                        upper = DmElement.upperDefault
                    lower = dr.lower
                    if dr.lower is None: # lower values sometimes set to None
                        lower = DmElement.lowerDefault
                    self.initDmElement(atm1, atm2, lower, upper)
                else:
                    # ambiguous restraints
                    rm6distances = self._calculateAverage( dr )
                    if rm6distances is None:
                        nTwarning('Queeny.initRestraints: failure to analyze %s', dr)
                        break
                    #endif

                    upper = dr.upper
                    if dr.upper is None: # sometimes happens; i.e. entry 1but
                        upper = DmElement.upperDefault
                    lower = dr.lower
                    if dr.lower is None: # lower values sometimes set to None
                        lower = DmElement.lowerDefault

                    # hr: total uncertainty change associated with this restraint
                    # dH = hmax - hr : change in uncertainty
                    # hi = hmax - dH*frac : relative R-6 contribution of atom pair: sum(hi) = hr
                    hmax = DmElement.uncertaintyDefault
                    hr = math.log(upper-lower)
                    dH = hmax - hr
                    pair = 0
                    for atm1,atm2 in dr.atomPairs:
                        hi = hmax - rm6distances[pair]*dH
                        self.initDmElement(atm1, atm2, lower, lower+math.exp(hi))
                        pair += 1
                    #end for
                #end if
#                count += 1
            #end for
        #end for

#        nTdebug('Queeny.initRestraints: %d restraints added (# elements = %d)', count, len(self))

        self.setNeighbors(nkeys) # update the neighbors for newly added
Пример #13
0
def runShiftx(project, parseOnly=False, model=None):
    """
    Use shiftx program to predict chemical shifts
    Works only for protein residues.

    Adds ShiftxResult instance to validation container of atoms
    LEGACY:
    Adds a NTlist object with predicted values for each model as shiftx attribute
    to each atom for which there are predictions, or empty list otherwise.

    Throws warnings for non-protein residues.
    Returns True on error.

    Shiftx works on pdb files, uses only one model (first), so we have to write the files separately and analyze them
    one at the time.
    """

    # LEGACY:
    if parseOnly:
        return parseShiftx(project)

    if cdefs.cingPaths.shiftx is None:
        nTmessage("runShiftx: no shiftx executable, skipping")
        return False  # Gracefully return

    if project.molecule is None:
        nTerror("runShiftx: no molecule defined")
        return True

    if project.molecule.modelCount == 0:
        nTwarning('runShiftx: no models for "%s"', project.molecule)
        return True

    if model is not None and model >= project.molecule.modelCount:
        nTerror('runShiftx: invalid model (%d) for "%s"', model, project.molecule)
        return True

    if not project.molecule.hasAminoAcid():
        nTmessage("==> Skipping runShiftx because no amino acids are present.")
        return False

    nTmessage("==> Running shiftx")

    skippedAtoms = []  # Keep a list of skipped atoms for later
    skippedResidues = []  # Only used for presenting to end user not actually used for skipping.
    skippedChains = []

    for chain in project.molecule.allChains():
        skippChain = True
        for res in chain.allResidues():
            if not res.hasProperties("protein"):
                if not res.hasProperties("HOH"):  # don't report waters
                    skippedResidues.append(res)
                for atm in res.allAtoms():
                    atm.pdbSkipRecord = True
                    skippedAtoms.append(atm)
                # end for
            else:
                skippChain = False
            # end if
            if skippChain:
                skippedChains.append(chain)
        # end for
    # end for
    if skippedResidues:
        nTmessage("==> runShiftx: %s non-protein residues will be skipped." % len(skippedResidues))

    defs = project.getStatusDict(constants.SHIFTX_KEY, **shiftxStatus())
    if model is not None:
        defs.models = NTlist(model)
    else:
        defs.models = NTlist(*range(project.molecule.modelCount))
    defs.baseName = "model_%03d"
    defs.completed = False
    defs.parsed = False
    defs.chains = []

    # initialize the shiftx attributes
    _resetShiftx(project)

    path = project.validationPath(defs.directory)
    if not path:
        return True
    if path.exists():
        nTdebug("runShiftx: removing %s with prior data", path)
        path.rmdir()
    path.makedirs()

    doShiftx = ExecuteProgram(pathToProgram=cdefs.cingPaths.shiftx, rootPath=path, redirectOutput=False)
    startTime = io.now()

    for model in defs.models:
        # set filenames
        rootname = defs.baseName % model
        nTdebug("runShiftx: doing model %s, path %s, rootname %s", model, path, rootname)

        # generate a pdbfile
        pdbFile = project.molecule.toPDB(model=model, convention=constants.IUPAC)
        if not pdbFile:
            nTerror("runShiftx: Failed to generate a pdb file for model: %s", model)
            return True
        pdbFile.save(path / rootname + ".pdb")
        del pdbFile

        for chain in project.molecule.allChains():
            if chain not in skippedChains:
                # nTdebug('Doing chain code [%s]' % (chain.name))
                # quotes needed because by default the chain id is a space now.
                # chainId =  "'" + chain.name + "'"
                # According to the readme in shiftx with the source this is the way to call it.
                chainId = "1" + chain.name
                outputFile = rootname + "_" + chain.name + ".out"
                defs.chains.append((chain.name, outputFile))
                doShiftx(chainId, rootname + ".pdb", outputFile)
            # end if
        # end for
    # end for

    # cleanup
    for pdbfile in path.glob("*.pdb"):
        pdbfile.remove()

    # Restore the 'default' state
    for atm in skippedAtoms:
        atm.pdbSkipRecord = False

    defs.completed = True
    # parse the results
    if parseShiftx(project):
        return True

    defs.date = io.now()
    defs.version = __version__
    defs.molecule = project.molecule.asPid
    defs.remark = "Shiftx on %s completed in %.1f seconds on %s; data in %s" % (
        project.molecule,
        defs.date - startTime,
        defs.date,
        path,
    )
    project.history(defs.remark)
    nTmessage("==> %s", defs.remark)
    return False
Пример #14
0
def runTalosPlus(project, tmp=None, parseOnly=False):
    """Perform a talos+ analysis; parses the results; put into new CING dihedral restraint list
    Returns True on error.
    Returns False when talos is absent or when all is fine.
    """
    #LEGACY:
    if parseOnly:
        return parseTalosPlus(project)

    if project is None:
        nTerror("runTalosPlus: No project defined")
        return True

    # check executable
    if cdefs.cingPaths.talos is None:
        nTmessage('runTalosPlus: no talosPlus executable defined, skipping')
        return False # Gracefully return
    status, output = getOsResult('which '+ cdefs.cingPaths.talos )
    if len(output) == 0:
        nTmessage('runTalosPlus: invalid talosPlus executable defined (%s), skipping', cdefs.cingPaths.talos)
        return False # Gracefully return

    if project.molecule is None:
        nTmessage("runTalosPlus: No molecule defined")
        return True

    residues = project.molecule.residuesWithProperties('protein')
    if not residues:
        nTmessage('runTalosPlus: no amino acids defined')
        return False

    if len( project.molecule.resonanceSources ) == 0:
        nTmessage("==> runTalosPlus: No resonances defined so no sense in running.")
        # JFD: This doesn't catch all cases.
        return False

    talosDefs = project.getStatusDict(constants.TALOSPLUS_KEY, **talosDefaults)

    talosDefs.molecule = project.molecule.asPid
    talosDefs.directory = constants.TALOSPLUS_KEY

    path = project.validationPath( talosDefs.directory )
    if not path:
        return True
    if path.exists():
        nTdebug('runTalosPlus: removing %s with prior data', path)
        path.rmdir()
    path.makedirs()

    startTime = io.now()

    talosDefs.completed = False
    talosDefs.parsed = False
    _resetTalosPlus(project)

    # Exporting the shifts
    fileName = path / talosDefs.tableFile
    if exportShifts2TalosPlus(project, fileName=fileName):
        nTwarning("runTalosPlus: Failed to exportShifts2TalosPlus; this is normal for empty CS list.")
        return False

    # running TalosPlus
    talosProgram = ExecuteProgram(cdefs.cingPaths.talos, rootPath=path, redirectOutput=True)
    nTmessageNoEOL('==> Running talos+ ... ')
    talosProgram( '-in ' + talosDefs.tableFile + ' -sum ' + talosDefs.predFile )
    nTmessage('Done!')

    if _findTalosOutputFiles(path, talosDefs):
        return True

    talosDefs.date = io.now()
    talosDefs.completed=True
    talosDefs.version = __version__
    talosDefs.molecule = project.molecule.asPid
    talosDefs.remark = 'TalosPlus on %s completed in %.1f seconds on %s; data in %s' % \
                       (project.molecule, talosDefs.date-startTime, talosDefs.date, path)

    # Importing the results
    if parseTalosPlus(project):
        nTerror("runTalosPlus: Failed parseTalosPlus")
        return True

    project.history(talosDefs.remark)
    nTmessage('==> %s', talosDefs.remark)
    return False
Пример #15
0
def exportShifts2TalosPlus( project, fileName=None):
    """Export shifts to TalosPlus format

    Return True on error including situation where no shifts were added to the file.

---------------------------------------------------

An example of the required shift table format is shown below. Complete examples can be found in the talos/shifts and talos/test directories. Specifically:

In the current version of TALOS/TALOS+, residue numbering must begin at 1.
The protein sequence should be given as shown, using one or more "DATA SEQUENCE" lines. Space characters in the sequence will be ignored. Use "c" for oxidized CYS (CB ~ 42.5 ppm) and "C" for reduced CYS (CB ~ 28 ppm) in both the sequence header and the shift table.
The table must include columns for residue ID, one-character residue name, atom name, and chemical shift.
The table must include a "VARS" line which labels the corresponding columns of the table.
The table must include a "FORMAT" line which defines the data type of the corresponding columns of the table.
Atom names are always given exactly as:
    HA       for H-alpha of all residues except glycine
    HA2      for the first H-alpha of glycine residues
    HA3      for the second H-alpha
    C        for C' (CO)
    CA       for C-alpha
    CB       for C-beta
    N        for N-amide
    HN       for H-amide
As noted, there is an exception for naming glycine assignments, which should use HA2 and HA3 instead of HA. In the case of glycine HA2/HA3 assignments, TALOS/TALOS+ will use the average value of the two, so that it is not necessary to have these assigned stereo specifically ; for use of TALOS/TALOS+, the assignment can be arbitrary. Note however that the assignment must be given exactly as either "HA2" or "HA3" rather than "HA2|HA3" etc.
Other types of assignments may be present in the shift table; they will be ignored.

Example shift table (excerpts):

   REMARK Ubiquitin input for TALOS, HA2/HA3 assignments arbitrary.

   DATA SEQUENCE MQIFVKTLTG KTITLEVEPS DTIENVKAKI QDKEGIPPDQ QRLIFAGKQL
   DATA SEQUENCE EDGRTLSDYN IQKESTLHLV LRLRGG

   VARS   RESID RESNAME ATOMNAME SHIFT
   FORMAT %4d   %1s     %4s      %8.3f

     1 M           HA                  4.23
     1 M           C                 170.54
     1 M           CA                 54.45
     1 M           CB                 33.27
     2 Q           N                 123.22
     2 Q           HA                  5.25
     2 Q           C                 175.92
     2 Q           CA                 55.08
     2 Q           CB                 30.76
---------------------------------------------------

From talos+ randcoil.tab file:

REMARK Talos Random Coil Table 2005.032.16.15
REMARK Cornilescu, Delaglio and Bax
REMARK CA/CB from Spera, Bax, JACS 91.
REMARK Others from Wishart et al. J. Biomol. NMR, 5(1995), 67-81
REMARK Pro N shift is the current database average (7 residues).
REMARK HIS = Wishart's val - 0.5*(diff between prot./non-prot. Howarth&Lilley)

DATA RESNAMES  A C c D E F G H I K L M N P Q R S T V W Y
DATA ATOMNAMES HA CA CB C N HN

#
# Values for C are CYS-reduced.
# Values for c are CYS-oxidized.
# Values for H are HIS-unprotonated.
# Values for h are for HIS-protonated.
# Values for D and E are for protonated forms.
---------------------------------------------------

    """

    if not project:
        return True
    #end if

    if not project.molecule:
        nTerror('exportShifts2TalosPlus: no molecule defined')
        return True
    molecule = project.molecule
    residues = molecule.residuesWithProperties('protein')
    if not residues:
        nTerror('exportShifts2TalosPlus: no amino acid defined')
        return True

    table = NmrPipeTable()
    table.remarks.append( sprintf('shifts from %s', molecule.name ) )
    residueOffset = residues[0].resNum-1 # residue numbering has to start from 1
    table.remarks.append( sprintf('residue numbering offset  %d', residueOffset ) )

#   generate a one-letter sequence string; map 'all chains to one sequence'
    seqString = ''
    for res in residues:
#        seqString = seqString + res.db.shortName JFD mod; wrong, look at format def above!
        if res.translate(constants.INTERNAL_0) == 'CYSS':
            seqString = seqString + 'c' # oxidized
        else:
            seqString = seqString + res.db.shortName
    #end for

#   data
    table.data.SEQUENCE = seqString

#   add collun entries
    table.addColumn('RESID',    '%-4d')
    table.addColumn('RESNAME',  '%-4s')
    table.addColumn('ATOMNAME', '%-4s')
    table.addColumn('SHIFT',    '%8.3f')

    # defines IUPAC to talos mapping and nuclei used
    talosDict = dict(
                 N  = 'N',
                 H  = 'HN',
                 CA = 'CA',
                 HA = 'HA',
                 HA2= 'HA2',
                 HA3= 'HA3',
                 QA = 'HA2,HA3', # QA will be translsate into real atoms
                 CB = 'CB',
                 C  = 'C'
                 )
    talosNuclei = talosDict.keys()

    atmCount = 0
    for resId,res in enumerate(residues):
        for ac in res.allAtoms():
            atomName = ac.translate(constants.IUPAC)
            if (ac.isAssigned(resonanceListIdx=constants.RESONANCE_LIST_IDX_ANY) and ( atomName in talosNuclei)):
                shift = ac.shift(resonanceListIdx=constants.RESONANCE_LIST_IDX_ANY) # save the shift, because Gly QA pseudo atom does get expanded
                for ra in ac.realAtoms():
                    atomName = ra.translate(constants.IUPAC)
                    # Translate to TalosPlus
                    if talosDict.has_key(atomName):
                        atomName = talosDict[atomName]
                    else:
                        nTerror('exportShifts2TalosPlus: strange, we should not be here (ra=%s)', ra)
                        continue
                    #end if

                    #print '>', seqString[resId:resId+1]
                    table.addRow( RESID=resId+1, RESNAME=seqString[resId:resId+1], ATOMNAME=atomName, SHIFT=shift)
                    atmCount += 1
                #end for
            #end if
        #end for
    #end for

    # save the table
    if not fileName:
        fileName = molecule.name + '.tab'
    if not table.writeFile(fileName):
        nTmessage( '==> exportShifts2TalosPlus:  %-4d shifts   written to "%s"', atmCount, fileName )
    if atmCount == 0:
        return True