Beispiel #1
0
def getBondedDimsDict(spectrum):

    bondedDims = {}
    for dataDim1, dataDim2 in getOnebondDataDims(spectrum):
        i = dataDim1.dim - 1
        j = dataDim2.dim - 1
        bondedDims[i] = j
        bondedDims[j] = i

    return bondedDims
Beispiel #2
0
    def apply_round_tolerance(self, tolerances):
        '''Evaluates dimensions corresponding to a through-bond
           transfer together and uses the deviation between the
           expected peak position (based on the assigned chemical
           shifts) and the actual peak position to make the
           tolerance 'round' instead of square. All possible
           contributions to the restraints that deviate from
           the expected peak position within twice the deviation
           of the least deviating contribution or deviate less
           than half of the maximum deviation still within the
           tolerance square (2**0.5/2) are kept. All other
           options are removed.

        '''

        if not self.contributions:
            return

        tolerances = dict([(dim, maxtol) for dim, mintol, maxtol, multi in tolerances])
        shiftList = self.peak.peakList.dataSource.experiment.shiftList
        peakDims = self.peak.sortedPeakDims()
        spectrum = self.peak.peakList.dataSource

        for dataDims in getOnebondDataDims(spectrum):
            distances = []
            for contrib in self.contributions:
                sum_of_squares = 0
                for dataDim in dataDims:
                    dim = dataDim.dim
                    peakDim = peakDims[dim-1]
                    resonance = contrib.assignment[dim-1]
                    tolerance = tolerances[dataDim]
                    v1 = resonance.findFirstShift(parentList=shiftList).value
                    v2 = peakDim.value
                    delta_squared = ((v1-v2)/tolerance)**2
                    sum_of_squares += delta_squared
                distances.append(sum_of_squares**0.5)

            closest = min(distances)
            for distance, contrib in zip(distances, self.contributions):
                if distance > 2*closest and distance > 2**0.5/2:
                    self.contributions.remove(contrib)
def analysePeaks(peakList):


  # Row per peak - Cols: height vs PL mean, vol vs PL mean,
  # sign check, F1 shift delta, F2 shift delta ++

  meanVolume = 0.0
  meanHeight = 0.0
  meanVolume2 = 0.0
  meanHeight2 = 0.0
  nVolume = 0
  nHeight = 0

  pos = 0
  tot = 0

  data0 = []
  for peak in peakList.sortedPeaks():
    
    volume = getPeakVolume(peak)
    height = getPeakHeight(peak)
    logVolume = None
    logHeight = None
        
    if volume:
      logVolume = log(abs(volume))
      meanVolume += logVolume
      meanVolume2 += logVolume * logVolume
      nVolume += 1
    else:
      volume = 0.0
    
    if height:
      logHeight = log(abs(height))
      meanHeight += logHeight
      meanHeight2 += logHeight * logHeight
      nHeight += 1
  
      tot += 1
      if height > 0:
        pos += 1
        
    else:
      height = 0.0
  
    data0.append([peak, volume,logVolume, height, logHeight])
  
  
  if nVolume:
    meanVolume /= float(nVolume)
    meanVolume2 /= float(nVolume)
  
  if nHeight:
    meanHeight /= float(nHeight)
    meanHeight2 /= float(nHeight)
  
  heightSd =  sqrt(abs(meanHeight2 - (meanHeight * meanHeight)))
  # abs() due to float point error - v small negs rather than zero 
  volumeSd =  sqrt(abs(meanVolume2 - (meanVolume * meanVolume)))
    
  shiftList = peakList.dataSource.experiment.shiftList
  
  boundDataDims = {}
  for dataDim1, dataDim2 in getOnebondDataDims(peakList.dataSource):
    boundDataDims[dataDim1] = dataDim2
    boundDataDims[dataDim2] = dataDim1

  data = []
  for peak, volume, logVolume, height, logHeight in data0:
  
    deltaLogVolume = None
    deltaLogHeight = None
    
    if volume:
      deltaLogVolume = abs(logVolume - meanVolume)

    if height:
      deltaLogHeight = abs(logHeight - meanHeight)

    maxDeltas = []
    assignErrors = {}
    # Row per peak: - Cols: Causes muli 1bond,
    # causes atom impossible 1bond, Partly assigned onebond,
    # missing/extra assign
    for peakDim in peak.sortedPeakDims():
      
      maxDelta = None
      isotopeCode  = None
      
      if shiftList:
        for contrib in peakDim.peakDimContribs:
          resonance = contrib.resonance
          isotopeCode = resonance.isotopeCode
          shift = resonance.findFirstShift(parentList=shiftList)
          if shift:
            delta = abs(peakDim.realValue - shift.value)
            
            if (maxDelta is None) or (delta > maxDelta):
              maxDelta = delta
      
      maxDeltas.append([maxDelta,isotopeCode])  
      
      for contrib in peakDim.peakDimContribs:
        resonance = contrib.resonance
        # Warn the user if prochirals have similar shifts
        # but only one is linked to the peak.
        resonanceSet = resonance.resonanceSet
        if resonanceSet:
          if len(resonanceSet.atomSets) > 1:
            resonances2 = list(resonanceSet.resonances)
            resonances2.remove(resonance)
            
            for resonance2 in resonances2:
              if peakDim.findFirstPeakDimContrib(resonance=resonance2):
                continue
              
              for contrib2 in resonance2.peakDimContribs:
                if contrib2.peakDim.peak.peakList is peakList:
                  break
              
              else:
                shift = resonance.findFirstShift(parentList=shiftList)
                shift2 = resonance2.findFirstShift(parentList=shiftList)
                if shift and shift2:
                  delta = abs(shift.value-shift2.value)
                  analysisDataDim = getAnalysisDataDim(peakDim.dataDim)
 
                  if analysisDataDim and (delta < analysisDataDim.assignTolerance/5.0):
                    name = makeResonanceGuiName(resonance2, fullName=False)
                    msg = 'Also expected dim %d %s assignment' % (peakDim.dim,name)
                    assignErrors[msg] = True

      boundDataDim = boundDataDims.get(peakDim.dataDim)
      if boundDataDim:
        peakDim2 = peak.findFirstPeakDim(dataDim=boundDataDim)
    
        if peakDim.peakDimContribs:
          if peakDim2.peakDimContribs:
            
            # Count number of atomSets to see if result makes sense
            atomSets1 = set()
            atomSets2 = set()
            for contrib2 in peakDim2.peakDimContribs:
              resonanceSet = contrib2.resonance.resonanceSet
              if resonanceSet:
                for atomSet in resonanceSet.atomSets:
                  atomSets2.add(atomSet)
          
            for contrib in peakDim.peakDimContribs:
              resonance = contrib.resonance
              resonanceSet = resonance.resonanceSet
              name = makeResonanceGuiName(resonance)
              bound = getBoundResonances(resonance, recalculate=True)
              impossibles  = []
              
              if resonanceSet:
                for atomSet in resonanceSet.atomSets:
                  atomSets1.add(atomSet)

              if bound:
        
                nBound = len(bound)
                
                for reson in bound:
                  # Correct for multiatom atomSets (CH3 resonances e,g,)
                  resSet = reson.resonanceSet
                  if resSet:
                    ll = [1]
                    for atomSet in resSet.atomSets:
                      length = len(atomSet.atoms)
                      cas = atomSet.findFirstAtom().chemAtom.chemAtomSet
                      if not cas or cas.isEquivalent is not None:
                        # Test excludes e.g. Tyr and Phe side chains
                        # that otherwise give spurious errors
                        ll.append(length)
                    # Add additional number of atoms for each bound resonance
                    nBound += min(ll) - 1
                    
                if isotopeCode in ('1H','2H','19F'):
                  if nBound > 1:
                    assignErrors['%s mutiple %s bonds' % (name,isotopeCode)] = True
 
                elif resonanceSet:
                  chemAtom = resonance.resonanceSet.findFirstAtomSet().findFirstAtom().chemAtom
                  if nBound > len(chemAtom.chemBonds):
                    assignErrors['%s excessive bonds' % name] = True
                    
                okAtoms = False
                foundBound = False
                for contrib2 in peakDim2.peakDimContribs:
                  resonance2 = contrib2.resonance
                  
                  if resonance2 in bound:
                    resonanceSet2 = resonance2.resonanceSet
                    foundBound = True
                    if resonanceSet and resonanceSet2:
                      if not areResonanceSetAtomsBound(resonanceSet, resonanceSet2):
                        names = [name,makeResonanceGuiName(resonance2)]
                        names.sort()
                        impossibles.append(names)
                        
                      else:
                        okAtoms = True
                        
                if not okAtoms:
                  for names in impossibles:
                    assignErrors['Impossible bond %s-%s' % tuple(names)] = True
                
                if not foundBound:
                  assignErrors['%s no bound partner' % name] = True
            
            if isotopeCode in ('1H','2H','19F'):
              if len(atomSets2) > len(atomSets1):
                assignErrors['Only %s H to bind %s Non-H' % (len(atomSets1), len(atomSets2))] = True
                
          else:
            assignErrors['Dim %d empty' % peakDim2.dataDim.dim] = True
        
        elif peakDim2.peakDimContribs:
          assignErrors['Dim %d empty' % peakDim.dataDim.dim] = True
      

    sortedPeakDims = peak.sortedPeakDims()
    annotation = ' '.join([pd.annotation or '-' for pd in sortedPeakDims])
    
    locations = []
    for pd in sortedPeakDims:
      if pd.value is None:
        if pd.position is None:
          locations.append('Error')
        else:
          locations.append('%.2f' % pd.position)
          
      else:
        locations.append('%.2f' % pd.value)
    
    location   = ' '.join(locations)

    nearestPeak, nearestDistance = findClosestOtherPeak(peak, scale=2.0)
    symmetryPeaks = findSymmetryPeaks(peak)

    datum = [peak.serial,annotation,assignErrors.keys(),location,
             volume,deltaLogVolume,
             height,deltaLogHeight,maxDeltas,
             nearestPeak, nearestDistance, symmetryPeaks]
    data.append([peak, datum])

  posProp = 1.0
  if tot:
    posProp = pos/float(tot)

  return posProp, volumeSd, heightSd, data
Beispiel #4
0
    def predictAmidePairs(self):

        self.amidePair = None
        isAmide = self.isAmide = {}
        amidePairs = self.amidePairs = []
        peakList = self.peakList

        if peakList:
            tol = self.tolEntry.get() or 0.001
            spectrum = self.peakList.dataSource
            dimPairs = getOnebondDataDims(spectrum)

            if not dimPairs:
                return

            isotopes = getSpectrumIsotopes(spectrum)

            hDim = None
            for dimA, dimB in dimPairs:
                i = dimA.dim - 1
                j = dimB.dim - 1

                if ('1H' in isotopes[i]) and ('1H' not in isotopes[j]):
                    hDim = i
                    nDim = j

                elif ('1H' in isotopes[j]) and ('1H' not in isotopes[i]):
                    hDim = j
                    nDim = i

                if hDim is not None:
                    break

            isAssigned = {}
            matchPeaks = []
            for peak in peakList.peaks:
                if isAmide.get(peak):
                    continue

                peakDims = peak.sortedPeakDims()
                ppmN = peakDims[nDim].realValue
                ppmH = peakDims[hDim].realValue

                for contrib in peakDims[nDim].peakDimContribs:
                    resonance = contrib.resonance

                    for contrib2 in resonance.peakDimContribs:
                        peak2 = contrib2.peakDim.peak
                        if (peak2.peakList is peakList) and (peak2
                                                             is not peak):
                            isAmide[peak] = peak2
                            isAmide[peak2] = peak

                            if hasattr(peak, 'amideConfirmed'):
                                peak2.amideConfirmed = peak.amideConfirmed
                            else:
                                peak.amideConfirmed = True
                                peak2.amideConfirmed = True

                            ppmH2 = peak2.findFirstPeakDim(dim=hDim + 1).value
                            ppmN2 = peak2.findFirstPeakDim(dim=nDim + 1).value
                            ppmNm = 0.5 * (ppmN + ppmN2)
                            deltaN = abs(ppmN - ppmN2)
                            amidePairs.append(
                                (peak, peak2, deltaN, ppmNm, ppmH, ppmH2))
                            break

                    else:
                        continue
                    break

                else:
                    if ppmN > 122.0:
                        continue
                    elif ppmN < 103:
                        continue

                    if ppmH > 9.0:
                        continue
                    elif ppmH < 5.4:
                        continue

                    if not hasattr(peak, 'amideConfirmed'):
                        peak.amideConfirmed = False
                    matchPeaks.append((ppmN, ppmH, peak))

            N = len(matchPeaks)
            unassignedPairs = []
            for i in range(N - 1):
                ppmN, ppmH, peak = matchPeaks[i]

                for j in range(i + 1, N):
                    ppmN2, ppmH2, peak2 = matchPeaks[j]

                    deltaN = abs(ppmN2 - ppmN)
                    if deltaN > tol:
                        continue

                    if abs(ppmH - ppmH2) > 1.50:
                        continue

                    ppmNm = 0.5 * (ppmN + ppmN2)

                    isAmide[peak] = peak2
                    isAmide[peak2] = peak
                    unassignedPairs.append(
                        (deltaN, peak, peak2, ppmNm, ppmH, ppmH2))

            unassignedPairs.sort()
            done = {}
            for deltaN, peak, peak2, ppmNm, ppmH, ppmH2 in unassignedPairs:
                if done.get(peak):
                    continue
                if done.get(peak2):
                    continue

                done[peak] = True
                done[peak2] = True

                amidePairs.append((peak, peak2, deltaN, ppmNm, ppmH, ppmH2))

            peaks = isAmide.keys()
            for peak in peaks:
                if done.get(peak) is None:
                    del isAmide[peak]

            self.updateAfter()
Beispiel #5
0
def linkSpinSystemInterIntraResonances(spinSystem,
                                       activeLists,
                                       tolerances=None):

    nmrProject = spinSystem.topObject
    prevSpinSystem = findConnectedSpinSystem(spinSystem)
    allowedRefExps, coRefExps = getSeqAssignRefExperiments(nmrProject.root)

    if not prevSpinSystem:
        if spinSystem.residue:
            residueB = getLinkedResidue(spinSystem.residue, linkCode='prev')
            prevSpinSystem = nmrProject.findFirstResonanceGroup(
                residue=residueB)

    if not prevSpinSystem:
        prevSpinSystem = nmrProject.newResonanceGroup()
        makeSeqSpinSystemLink(prevSpinSystem, spinSystem)

    peaks = []
    found = {}
    expTypes = {}
    linkDims = {}

    # go though the peaks associated with this spin system
    # within the selected peak lists
    # find the indirect, linking dimension
    for resonance in spinSystem.resonances:
        if resonance.isotopeCode != '15N':
            continue

        if not isResonanceAmide(resonance):
            continue

        for contrib in resonance.peakDimContribs:
            peakDim = contrib.peakDim
            peak = peakDim.peak

            if found.get(peak):
                continue

            peakList = peak.peakList
            if peakList not in activeLists:
                continue

            setupPeakHeight(peak)
            intensity = peak.findFirstPeakIntensity(intensityType='height')
            if not intensity:
                continue

            spectrum = peakList.dataSource
            expType = expTypes.get(peakList, spectrum.experiment.refExperiment)
            expTypes[peakList] = expType

            linkDim = linkDims.get(peakList)
            if linkDim is None:
                boundDict = {}
                for dataDimA, dataDimB in getOnebondDataDims(spectrum):
                    boundDict[dataDimA] = dataDimB
                    boundDict[dataDimB] = dataDimA

                dims = []
                for dataDim in spectrum.dataDims:
                    dataDimRef = getPrimaryDataDimRef(dataDim)

                    if not dataDimRef:
                        continue

                    isotopes = '/'.join(dataDimRef.expDimRef.isotopeCodes)
                    dims.append((isotopes, dataDim))

                dims.sort()
                for isotopes, dataDim in dims:
                    if '13C' == isotopes:
                        linkDim = (dataDim.dim, isotopes)
                        break

                    elif '1H' == isotopes:
                        dataDimB = boundDict.get(dataDim)

                        if dataDimB:
                            dataDimRefB = getPrimaryDataDimRef(dataDimB)

                            if '15N' in dataDimRefB.expDimRef.isotopeCodes:
                                continue

                        linkDim = (dataDim.dim, isotopes)
                        break

                linkDims[peakList] = linkDim

            if linkDim is None:
                continue

            if peakDim.dim is linkDim[0]:
                continue

            found[peak] = True
            peakDimL = peak.findFirstPeakDim(dim=linkDim[0])
            isotope = linkDim[1]

            peaks.append((peak, expType, peakDimL, isotope, intensity.value))

    peakTypes = []
    interResonances = {}
    intraResonances = {}
    for peak, expType, peakDim, isotope, height in peaks:

        ppm = peakDim.value
        atomType = None

        if expType in coRefExps:
            isInter = True
        elif 'N[coca]' in expType.name:
            isInter = False
        elif 'N_' in expType.name:
            isInter = False
        else:
            isInter = None

        resonance = None
        contrib = peakDim.findFirstPeakDimContrib()
        if contrib:
            resonance = contrib.resonance

            if isInter and resonance.assignNames:
                atomType = resonance.assignNames[0]
                interResonances[atomType] = (resonance, ppm, prevSpinSystem)

            if (isInter is False) and resonance.assignNames:
                atomType = resonance.assignNames[0]
                intraResonances[atomType] = (resonance, ppm, spinSystem)

        if not atomType:
            atomType = guessAtomType(expType, ppm, height)

        peakTypes.append((peakDim, atomType, contrib, isotope, isInter, ppm))

    # Get known inter/intra resonance assignments
    # for each atom type
    for peakDim, atomType, contrib, isotope, isInter, ppm in peakTypes:
        if isInter is None:
            continue
        elif isInter:
            resDict = interResonances
            uniqSpinSystem = prevSpinSystem
        else:
            resDict = intraResonances
            uniqSpinSystem = spinSystem

        if atomType:
            resonance, ppm2, ss = resDict.get(atomType, (None, None, None))

            if (resonance is None) and contrib:
                resDict[atomType] = (contrib.resonance, ppm, uniqSpinSystem)

    # Make any new assignments for the unambig peaks
    untypedIntra = []
    untypedInter = []
    for i, data in enumerate(peakTypes):
        (peakDim, atomType, contrib, isotope, isInter, ppm) = data

        if isInter is None:
            continue

        elif isInter:
            untyped = untypedInter
            resDict = interResonances
            uniqSpinSystem = prevSpinSystem

        else:
            resDict = intraResonances
            untyped = untypedIntra
            uniqSpinSystem = spinSystem

        if atomType:
            # Get any previously used resonances for this atom type
            resonance, ppm2, ss = resDict.get(atomType, (None, None, None))

            # If no prev resonance, look at the peak assignment
            if (not resonance) and contrib:
                resonance = contrib.resonance

            # Check to ensure that any existing resonance
            # is from the correct, this/prev spin system
            if resonance:
                spinSystem2 = resonance.resonanceGroup
                if spinSystem2 and (spinSystem2 is not uniqSpinSystem):
                    resonance = None

            # If no valid reasonance yet, check named ones
            # in the required spin system
            if not resonance:
                for resonance2 in uniqSpinSystem.resonances:
                    if atomType in resonance2.assignNames:
                        resonance = resonance2
                        break

            # If no valid resonance yet, make a new one
            if not resonance:
                resonance = nmrProject.newResonance(isotopeCode=isotope)

            # If peak dim is assigned to the wrong resonance
            # clear the assignment
            if contrib and contrib.resonance is not resonance:
                clearPeakDim(peakDim)
                contrib = None

            # Ensure the peak dim is assigned correctly
            if not contrib:
                assignResToDim(peakDim, resonance)

            # Check type of resonance set correctly
            if not resonance.assignNames:
                assignResonanceType(resonance, assignNames=(atomType, ))

            # Check spin system set correctly
            if resonance.resonanceGroup is not uniqSpinSystem:
                addSpinSystemResonance(uniqSpinSystem, resonance)

            # Store resonance by atom type, so that it can
            # be picked up later
            resDict[atomType] = (resonance, ppm, uniqSpinSystem)

        else:
            untyped.append((peakDim, contrib, isotope, isInter, ppm, i))

    uniqResonances = interResonances.values() + intraResonances.values()

    # Cluster untyped peaks
    interCluster = {}
    intraCluster = ()

    interData = (untypedInter, interCluster, prevSpinSystem)
    intraData = (untypedIntra, intraCluster, spinSystem)

    for untyped, clusters, uniqSpinSystem in (interData, intraData):

        for peakDim, contrib, isotope, isInter, ppm, i in untyped:

            if peakDim in clusters:
                continue

            if tolerances:
                tolerance = tolerances[isotope]
            else:
                tolerance = getAnalysisDataDim(peakDim.dataDim).assignTolerance

            if contrib:
                resonance = contrib.resonance
            else:
                resonance = None

            cluster = [peakDim]
            interCluster[peakDim] = True
            for peakDimB, contribB, isotopeB, isInterB, ppmB, i in untyped:
                if isotope != isotopeB:
                    continue

                if abs(ppmB - ppm) > tolerance:
                    continue

                if peakDim.peak.peakList == peakDimB.peak.peakList:
                    continue

                if contribB:
                    if not resonance:
                        resonance = contribB.resonance
                    if resonance is not contribB.resonance:
                        clearPeakDim(peakDimB)
                        assignResToDim(peakDimB,
                                       resonance,
                                       tolerance=tolerance)

                cluster.append(peakDimB)
                clusters[peakDimB] = True

            if not resonance:
                # Try to macth to any typed peaks
                for peakDimB, atomType, contribB, isotopeB, isInterB, ppmB in peakTypes:
                    if not contribB:
                        continue

                    if not atomType:
                        continue

                    if isotope != isotopeB:
                        continue

                    if abs(ppmB - ppm) > tolerance:
                        continue

                    if peakDim.peak.peakList == peakDimB.peak.peakList:
                        continue

                    resonance = contribB.resonance

                    # Set type for this peak
                    peakTypes[i] = (peakDim, atomType, contrib, isotope,
                                    isInter, ppm)
                    break

            if not resonance:
                resonance = nmrProject.newResonance(isotopeCode=isotope)

            for peakDimC in cluster:
                clearPeakDim(peakDimC)
                assignResToDim(peakDimC, resonance, tolerance=tolerance)

            uniqResonances.append((resonance, ppm, uniqSpinSystem))

            if resonance.resonanceGroup is not uniqSpinSystem:
                addSpinSystemResonance(uniqSpinSystem, resonance)

    # E.g. Find the HNCA peaks which best match the HNcoCA/iHNCA resonances
    matchDict = {}
    closestDict = {}
    for peakDim, atomType, contrib, isotope, isInter, ppm in peakTypes:
        if isInter is not None:
            # Not through HNcoCA or iHNCA
            continue

        if tolerances:
            tolerance = tolerances[isotope]
        else:
            tolerance = getAnalysisDataDim(peakDim.dataDim).assignTolerance

        matches = []
        shiftList = peakDim.peak.peakList.dataSource.experiment.shiftList
        for resonanceB, ppm2, uniqSpinSystem in uniqResonances:

            if resonanceB.isotopeCode != isotope:
                continue

            assignNames = resonanceB.assignNames
            if assignNames and (atomType not in assignNames):
                continue

            if not ppm2:
                shift = resonanceB.findFirstShift(parentList=shiftList)
                if not shift:
                    continue
                ppm2 = shift.value

            delta = abs(ppm - ppm2)
            prevDelta = closestDict.get(peakDim)
            if (prevDelta is None) or (delta < prevDelta):
                closestDict[peakDim] = delta

            if delta > tolerance:
                continue

            matches.append((delta, resonanceB, uniqSpinSystem))

        # Best match has smallest delta
        if matches:
            matches.sort()
            delta, resonance, uniqSpinSystem = matches[0]

            prevDelta, peakDimB, ss = matchDict.get(resonance,
                                                    (None, None, None))
            if not peakDimB or (delta < prevDelta):
                matchDict[resonance] = (delta, peakDim, uniqSpinSystem)

    uniqResonanceDict = {}
    for resonance in matchDict.keys():
        delta, peakDim, uniqSpinSystem = matchDict[resonance]
        uniqResonanceDict[peakDim] = resonance, uniqSpinSystem

    # E.g. go through HNCA peaks and assign to
    # HNcoCA or iHNCA resonances if required
    nonMatched = {}
    for peakDim, atomType, contrib, isotope, isInter, ppm in peakTypes:
        if isInter is not None:
            continue

        if closestDict.get(peakDim) is None:
            # No inter residue peaks at all
            intensity = peak.findFirstPeakIntensity(intensityType='height')

            if intensity:
                if not nonMatched.has_key(atomType):
                    nonMatched[atomType] = []
                nonMatched[atomType].append(
                    (-abs(intensity.value), peakDim, contrib, isotope))

            continue

        match = uniqResonanceDict.get(peakDim)
        if match:
            resonance, uniqSpinSystem = match

            if tolerances:
                tolerance = tolerances[isotope]
            else:
                tolerance = getAnalysisDataDim(peakDim.dataDim).assignTolerance

            # untyped through-carbonyl resonance can interit type
            # by matching typed peaks

            if atomType and not resonance.assignNames:
                resonance.addAssignName(atomType)

            # e.g. the HNcoCA resonance is the one to put on this HNCA peak
            if contrib:
                if contrib.resonance is not resonance:
                    clearPeakDim(peakDim)
                    assignResToDim(peakDim, resonance, tolerance=tolerance)
            else:
                assignResToDim(peakDim, resonance, tolerance=tolerance)

        else:
            # No match to an unambig peak
            # store to work out which is best for each atom type
            # e.g. just in case we have two intra residue CA possibilites
            if not nonMatched.has_key(atomType):
                nonMatched[atomType] = []

            nonMatched[atomType].append(
                (closestDict[peakDim], peakDim, contrib, isotope))

    for atomType in nonMatched.keys():

        if (atomType in intraResonances) and (atomType in interResonances):
            # Both resonances already found, peak is spurious
            continue

        if (atomType in intraResonances) and (atomType not in interResonances):
            # E.g. there was iHNCA but no HNcoCA
            otherSpinSystem = prevSpinSystem

        elif (atomType not in intraResonances) and (atomType
                                                    in interResonances):
            # E.g. there was HNcoCA but no iHNCA
            otherSpinSystem = spinSystem

        else:
            # No info
            continue

        peakDimList = nonMatched[atomType]
        peakDimList.sort()
        # Assume that if we have two possible ambiguous peaks
        # for any given atom type, then the one furthest from an
        # unambigous peak is the best to take
        deltaPpm, peakDim, contrib, isotope = peakDimList[-1]

        if tolerances:
            tolerance = tolerances[isotope]
        else:
            tolerance = getAnalysisDataDim(peakDim.dataDim).assignTolerance

        # E.g. this HNCA peak matches no HNcoCA/iHNCA resonances
        resonance = None
        if contrib:
            resonance = contrib.resonance

        if resonance:
            # Already have an assignment
            spinSystem2 = resonance.resonanceGroup
            if spinSystem2 is None:
                addSpinSystemResonance(otherSpinSystem, resonance)

            elif spinSystem2 is not otherSpinSystem:
                resonance = nmrProject.newResonance(isotopeCode=isotope)
                addSpinSystemResonance(otherSpinSystem, resonance)
                clearPeakDim(peakDim)
                contrib = assignResToDim(peakDim,
                                         resonance,
                                         tolerance=tolerance)

        else:
            # Needs a new assignment
            for resonance2 in otherSpinSystem.resonances:
                if atomType in resonance2.assignNames:
                    resonance = resonance2
                    break

            if not resonance:
                resonance = nmrProject.newResonance(isotopeCode=isotope)
                contrib = assignResToDim(peakDim,
                                         resonance,
                                         tolerance=tolerance)
                addSpinSystemResonance(otherSpinSystem, resonance)

        if not contrib:
            # Resonance has come from the spin system due to its atomTypes
            contrib = assignResToDim(peakDim,
                                     resonance,
                                     tolerance=2 * tolerance)

        if not contrib:
            # Existing resonance on spin system was too far away
            resonance = nmrProject.newResonance(isotopeCode=isotope)
            contrib = assignResToDim(peakDim, resonance, tolerance=tolerance)
            addSpinSystemResonance(otherSpinSystem, resonance)

        if atomType:
            assignNames = (atomType, )
        else:
            assignNames = []

        if not resonance.assignNames:
            assignResonanceType(resonance, assignNames=assignNames)