Ejemplo n.º 1
0
def AngleFinder(Atom1,Atom2,Atom3):
	vector1 = Atom1.get_vector()
	vector2 = Atom2.get_vector()
	vector3 = Atom3.get_vector()
	angle = bp.calc_angle(vector1,vector2,vector3)

	return math.degrees(angle)
Ejemplo n.º 2
0
def w_pdb_1(segment1, segment2):
    '''Compute writhe contribution from two input line segments. Segments
    are pairs of vectors as returned by Biopython.'''
    #    t1 = time.time()
    s1 = segment1
    s2 = segment2
    #form unit vectors at the segment extremes:
    v13 = s2[0] - s1[0]
    v23 = s2[0] - s1[1]
    v24 = s2[1] - s1[1]
    v14 = s2[1] - s1[0]
    v = [v13, v23, v24, v14, v13, v23]
    e = []
    #    l13 = np.sqrt(PDB.Vector.__mul__(v13,v13))
    #    l23 = np.sqrt(PDB.Vector.__mul__(v23,v23))
    #    l24 = np.sqrt(PDB.Vector.__mul__(v24,v24))
    #    l14 = np.sqrt(PDB.Vector.__mul__(v14,v14))
    l13 = np.sqrt(v13 * v13)
    l23 = np.sqrt(v23 * v23)
    l24 = np.sqrt(v24 * v24)
    l14 = np.sqrt(v14 * v14)
    ls = [l13, l23, l24, l14]
    for l in ls:
        if l == 0.0:
            return 0
    e13 = v13 / l13
    e23 = v23 / l23
    e24 = v24 / l24
    e14 = v14 / l14
    e = [e13, e23, e24, e14, e13, e23]
    ##        n_vect = np.linalg.norm(vect)
    ##        if n_vect == 0: #if one of the vectors is zero the two segments lie in a plane
    ##            return 0
    #        else:
    #            n_vect = np.linalg.norm(vect)
    #            e.append(vect/n_vect)
    #    t2 = time.time()
    #compute the angles
    s = 0
    for i in range(1, len(e) - 1):
        a = e[i - 1]
        b = e[i]
        c = e[i + 1]
        theta = PDB.calc_angle(a, b, c)
        #        print "angle is %f" % theta
        #APPARENTLY THIS ANGLE IS ALWAYS POSITIVE AND BETWEEN O AND PI; PROBABLY THE ANGLE BETWEEN SEGMENT AB, AC (WITHOUT SIGN)
        s = s + theta


#        print "s is %f " % s
#    t3 = time.time
    w = np.sign(s) * 2 * np.pi - s
    #    t4 = time.time()
    #    print "t2-t1:", 10*(t2-t1)
    #    print "t3-t2:", 10*(t3-t2)
    #    print "t4-t3:",10*(t4-t3)
    return w
Ejemplo n.º 3
0
def get_pose_constraints(Pose,
                         MaxDist,
                         MinPositionSeperation,
                         SasaRadius,
                         SasaScale,
                         UpstreamGrep,
                         DownstreamGrep,
                         NeedHydrogen=True):
    '''  '''
    # AlexsSasaCalculator is from Alex's interface_fragment_matching
    # thanks Alex!
    #
    # This is used to give buried polar contacts more weight. Thanks Alex Ford!
    try:
        from interface_fragment_matching.utility.analysis import AtomicSasaCalculator
        # make instace of Alex's sasa calculator
        AlexsSasaCalculator = AtomicSasaCalculator(probe_radius=SasaRadius)
        ResidueAtomSasa = AlexsSasaCalculator.calculate_per_atom_sasa(Pose)
    except ImportError:
        ' Error: SASA weighting of contacts requires interface_fragment_matching from Alex Ford '

    # for making full atom kd tree
    ResAtmCoordLists = []
    # for translating from kd tree index to ( residue, atom ) coord
    ResAtmRecordLists = []

    # loop through all residue numbers
    for Res in range(1, Pose.n_residue() + 1):
        # remade for each residue
        AtmRecordList = []
        AtmCoordList = []
        # loop through residue's atom numbers
        for Atm in range(1, Pose.residue(Res).natoms() + 1):
            # add (residue, atom) coord to residue's list
            AtmRecordList.append((Res, Atm))
            # add atom xyz coord to residue's list
            AtmCoordList.append(
                np.array(list(Pose.residue(Res).atom(Atm).xyz())))

        # add residue's lists to respective global lists
        ResAtmCoordLists.extend(AtmCoordList)
        ResAtmRecordLists.extend(AtmRecordList)

    ResidueAtomArray = np.array(ResAtmCoordLists)
    ResidueAtomKDTree = spatial.KDTree(ResidueAtomArray)

    ResidueAtomNeighbors = ResidueAtomKDTree.query_ball_point(
        ResidueAtomArray, MaxDist)
    # ResidueAtomNearNeighbors = ResidueAtomKDTree.query_ball_point( ResidueAtomArray, 2.0 )
    ResidueAtomHydrogens = ResidueAtomKDTree.query_ball_point(
        ResidueAtomArray, 1.1)

    # holds constraints before printing
    AllConstraints = []
    # holds sorted cst
    AllBackboneBackboneCst = []
    AllBackboneSidechainCst = []
    AllSidechainSidechainCst = []

    # All contacts are from upstream to downstream residues to avoid double counting
    Upstream = []
    for UpIndex, UpXyzCoords in enumerate(ResAtmCoordLists):
        UpRes, UpAtm = ResAtmRecordLists[UpIndex]

        # # loop through residues storing info on oxygens
        # for UpRes in range( 1, Pose.n_residue() + 1 ):
        #   # loop through atoms
        #   for UpAtm in range( 1, Pose.residue(UpRes).natoms() + 1 ):
        UpName = Pose.residue(UpRes).atom_name(UpAtm).replace(' ', '')

        # skip virtual residues
        if Pose.residue(UpRes).is_virtual(UpAtm):
            continue

        #                                this guy
        #                                 /
        # checks upstream name           V
        if re.match(UpstreamGrep, UpName):
            # print '\n'*2
            # print 'UpRes, UpName', UpRes, UpName

            # get neighbors of upstream residues
            NeighborsOfUpstream = ResidueAtomNeighbors[UpIndex]

            # prep for loop
            Downstreams = []

            Constraints = []
            BackboneBackboneCst = []
            BackboneSidechainCst = []
            SidechainSidechainCst = []

            # ArbitrayOrderOfAtomNames = {}
            for DownIndex in NeighborsOfUpstream:
                # name presumes downstream, checks with if imediately below
                DownRes, DownAtm = ResAtmRecordLists[DownIndex]

                # checks that downstream residue is dowstream of upstream and passes min primary sequence spacing
                if DownRes - UpRes >= MinPositionSeperation:
                    DownName = Pose.residue(DownRes).atom_name(
                        DownAtm).replace(' ', '')

                    # skip if same atom
                    if UpRes == DownRes:
                        if UpName == DownName:
                            continue

                    # skip virtual residues
                    if Pose.residue(DownRes).is_virtual(DownAtm):
                        continue

                    # checks downstream name
                    if re.match(DownstreamGrep, DownName):
                        # print 'DownRes, DownName', DownRes, DownName

                        PotentialUpstreamHydrogens = ResidueAtomHydrogens[
                            UpIndex]
                        UpstreamHydrogens = []
                        # print 'PotentialUpstreamHydrogens', PotentialUpstreamHydrogens
                        for UpH_I in PotentialUpstreamHydrogens:
                            UpH_Res, UpH_Atm = ResAtmRecordLists[UpH_I]
                            UpH_Name = Pose.residue(UpH_Res).atom_name(
                                UpH_Atm).replace(' ', '')
                            # print 'UpH_Name', UpH_Name
                            if 'H' in UpH_Name:
                                UpstreamHydrogens.append(
                                    (UpH_Res, UpH_Atm, UpH_Name))
                            # print 'UpstreamHydrogens', UpstreamHydrogens

                        PotentialDownstreamHydrogens = ResidueAtomHydrogens[
                            DownIndex]
                        DownstreamHydrogens = []
                        # print 'PotentialDownstreamHydrogens', PotentialDownstreamHydrogens
                        for DownH_I in PotentialDownstreamHydrogens:
                            DownH_Res, DownH_Atm = ResAtmRecordLists[DownH_I]
                            DownH_Name = Pose.residue(DownH_Res).atom_name(
                                DownH_Atm).replace(' ', '')
                            # print 'DownH_Name', DownH_Name
                            if 'H' in DownH_Name:
                                DownstreamHydrogens.append(
                                    (DownH_Res, DownH_Atm, DownH_Name))
                            # print 'DownstreamHydrogens', DownstreamHydrogens

                        # check their is at least one hydrogen in system before adding constraint
                        if len(UpstreamHydrogens) or len(
                                DownstreamHydrogens) or NeedHydrogen == False:

                            # these trys / excepts seperate
                            # backbone-backbone from
                            # backbone-sidechain from
                            # sidechain-sidechain interactions
                            #
                            # in future maybe sort into seperate lists, shouldn't rely on ResidueAtomSasa to know what is in backbone
                            try:
                                UpstreamSasa = ResidueAtomSasa[UpRes][UpName]
                                DownstreamSasa = ResidueAtomSasa[DownRes][
                                    DownName]
                                AverageSasa = np.mean(
                                    [UpstreamSasa, DownstreamSasa])
                                BBBB = 1
                                BBSC = SCSC = 0
                            except KeyError:
                                # These lines handle backbone to sidechain interactions
                                # set weight equal to the most buried
                                try:
                                    UpstreamSasa = ResidueAtomSasa[UpRes][
                                        UpName]
                                    AverageSasa = SasaScale.FloorSasa
                                    BBSC = 1
                                    BBBB = SCSC = 0
                                except KeyError:
                                    try:
                                        DownstreamSasa = ResidueAtomSasa[
                                            DownRes][DownName]
                                        AverageSasa = SasaScale.FloorSasa
                                        BBSC = 1
                                        BBBB = SCSC = 0

                                    # set weight of side chain side chain equal to the most buried
                                    except KeyError:
                                        AverageSasa = SasaScale.CeilingSasa
                                        SCSC = 1
                                        BBSC = BBBB = 0

                            # use instance of sasa_scale to calculate weight based on avg sasa of N and O
                            SasaBasedWeight = SasaScale.weigh(AverageSasa)
                            # print
                            # print 'AverageSasa', AverageSasa
                            # print 'SasaBasedWeight', SasaBasedWeight

                            # print 'found downstream neighbor %s'%DownName
                            DownXyzCoords = np.array(
                                list(
                                    Pose.residue(DownRes).atom(DownAtm).xyz()))
                            # print 'DownRes, DownName', DownRes, DownName
                            # print 'DownXyzCoords', DownXyzCoords

                            # ## Get neighbors for angles and torsions to use with AtomPairs

                            SelectUpNeighbors = []
                            # iterates through upstream atom neighbors for references for angle
                            for UpNeighborIndex in NeighborsOfUpstream:
                                UpNeighborRes, UpNeighborAtm = ResAtmRecordLists[
                                    UpNeighborIndex]
                                UpNeighborName = Pose.residue(
                                    UpNeighborRes).atom_name(
                                        UpNeighborAtm).replace(' ', '')

                                # keep looking if neighbor is hyrdogen
                                if 'H' in UpNeighborName:
                                    continue

                                # skip virtual residues
                                if Pose.residue(UpNeighborRes).is_virtual(
                                        UpNeighborAtm):
                                    continue

                                # keep looking if neighbor is self
                                if UpNeighborName == UpName and UpNeighborRes == UpRes:
                                    continue
                                # keep looking if neighbor is downstream residue again
                                if UpNeighborName == DownName and UpNeighborRes == DownRes:
                                    continue
                                UpNeighborCoords = ResAtmCoordLists[
                                    UpNeighborIndex]
                                DistanceToNeighbor = solenoid_tools.vector_magnitude(
                                    UpXyzCoords - UpNeighborCoords)
                                SelectUpNeighbors.append(
                                    (DistanceToNeighbor, UpNeighborName,
                                     UpNeighborRes, UpNeighborCoords))

                            # sort by distance to atom, nearest first
                            SelectUpNeighbors.sort()
                            UpNeighbor1Tuple = SelectUpNeighbors[0]
                            UpNeighbor2Tuple = SelectUpNeighbors[1]
                            # print '\n'*2
                            # print 'UpRes, UpName', UpRes, UpName
                            # print 'UpstreamHydrogens', UpstreamHydrogens
                            # print 'SelectUpNeighbors', SelectUpNeighbors

                            # get neighbors of upstream residues
                            NeighborsOfDownstream = ResidueAtomNeighbors[
                                DownIndex]
                            SelectDownNeighbors = []
                            # iterates through upstream atom neighbors for references for angle
                            for DownNeighborIndex in NeighborsOfDownstream:
                                DownNeighborRes, DownNeighborAtm = ResAtmRecordLists[
                                    DownNeighborIndex]
                                DownNeighborName = Pose.residue(
                                    DownNeighborRes).atom_name(
                                        DownNeighborAtm).replace(' ', '')

                                # keep looking if neighbor is hyrdogen
                                if 'H' in DownNeighborName:
                                    continue

                                # skip virtual residues
                                if Pose.residue(DownNeighborRes).is_virtual(
                                        DownNeighborAtm):
                                    continue

                                # keep looking if neighbor is self
                                if DownNeighborName == DownName and DownNeighborRes == DownRes:
                                    continue
                                # keep looking if neighbor is upstream residue
                                if DownNeighborName == UpName and DownNeighborRes == UpRes:
                                    continue

                                DownNeighborCoords = ResAtmCoordLists[
                                    DownNeighborIndex]
                                DistanceToNeighbor = solenoid_tools.vector_magnitude(
                                    DownXyzCoords - DownNeighborCoords)
                                SelectDownNeighbors.append(
                                    (DistanceToNeighbor, DownNeighborName,
                                     DownNeighborRes, DownNeighborCoords))

                            # sort by distance to atom, nearest first
                            SelectDownNeighbors.sort()
                            DownNeighbor1Tuple = SelectDownNeighbors[0]
                            DownNeighbor2Tuple = SelectDownNeighbors[1]
                            # print 'DownRes, DownName', DownRes, DownName
                            # print 'DownstreamHydrogens', DownstreamHydrogens
                            # print 'SelectDownNeighbors', SelectDownNeighbors

                            Distance = solenoid_tools.vector_magnitude(
                                DownXyzCoords - UpXyzCoords)

                            DistanceCst = 'AtomPair %s %d %s %d SCALARWEIGHTEDFUNC %f HARMONIC %.2f 1.0' % (
                                UpName, UpRes, DownName, DownRes,
                                SasaBasedWeight, Distance)

                            # Use Biopython for angle and dihedral calculations
                            # here 'Vec' means PDB.Vector of atom's xyz coord
                            UpstreamVec = PDB.Vector(UpXyzCoords)
                            DownstreamVec = PDB.Vector(DownXyzCoords)

                            UpNeighbor1Vec = PDB.Vector(UpNeighbor1Tuple[3])
                            UpNeighbor2Vec = PDB.Vector(UpNeighbor2Tuple[3])
                            DownNeighbor1Vec = PDB.Vector(
                                DownNeighbor1Tuple[3])
                            DownNeighbor2Vec = PDB.Vector(
                                DownNeighbor2Tuple[3])

                            Angle1 = PDB.calc_angle(UpNeighbor1Vec,
                                                    UpstreamVec, DownstreamVec)
                            AngleCst1 = 'Angle %s %d %s %d %s %d SCALARWEIGHTEDFUNC %f CIRCULARHARMONIC %.2f 0.5' % (
                                UpNeighbor1Tuple[1], UpNeighbor1Tuple[2],
                                UpName, UpRes, DownName, DownRes,
                                SasaBasedWeight, Angle1)
                            Angle2 = PDB.calc_angle(UpstreamVec, DownstreamVec,
                                                    DownNeighbor1Vec)
                            AngleCst2 = 'Angle %s %d %s %d %s %d SCALARWEIGHTEDFUNC %f CIRCULARHARMONIC %.2f 0.5' % (
                                UpName, UpRes, DownName, DownRes,
                                DownNeighbor1Tuple[1], DownNeighbor1Tuple[2],
                                SasaBasedWeight, Angle2)

                            Torsion1 = PDB.calc_dihedral(
                                UpNeighbor2Vec, UpNeighbor1Vec, UpstreamVec,
                                DownstreamVec)
                            TorsionCst1 = 'Dihedral %s %d %s %d %s %d %s %d SCALARWEIGHTEDFUNC %f CIRCULARHARMONIC %.2f 0.5' % (
                                UpNeighbor2Tuple[1], UpNeighbor2Tuple[2],
                                UpNeighbor1Tuple[1], UpNeighbor1Tuple[2],
                                UpName, UpRes, DownName, DownRes,
                                SasaBasedWeight, Torsion1)
                            Torsion2 = PDB.calc_dihedral(
                                UpNeighbor1Vec, UpstreamVec, DownstreamVec,
                                DownNeighbor1Vec)
                            TorsionCst2 = 'Dihedral %s %d %s %d %s %d %s %d SCALARWEIGHTEDFUNC %f CIRCULARHARMONIC %.2f 0.5' % (
                                UpNeighbor1Tuple[1], UpNeighbor1Tuple[2],
                                UpName, UpRes, DownName, DownRes,
                                DownNeighbor1Tuple[1], DownNeighbor1Tuple[2],
                                SasaBasedWeight, Torsion2)
                            Torsion3 = PDB.calc_dihedral(
                                UpstreamVec, DownstreamVec, DownNeighbor1Vec,
                                DownNeighbor2Vec)
                            TorsionCst3 = 'Dihedral %s %d %s %d %s %d %s %d SCALARWEIGHTEDFUNC %f CIRCULARHARMONIC %.2f 0.5' % (
                                UpName, UpRes, DownName, DownRes,
                                DownNeighbor1Tuple[1], DownNeighbor1Tuple[2],
                                DownNeighbor2Tuple[1], DownNeighbor2Tuple[2],
                                SasaBasedWeight, Torsion3)

                            # adds constraint to running lists of constraints
                            Constraints.extend([
                                DistanceCst, AngleCst1, AngleCst2, TorsionCst1,
                                TorsionCst2, TorsionCst3
                            ])
                            if BBBB:
                                BackboneBackboneCst.extend([
                                    DistanceCst, AngleCst1, AngleCst2,
                                    TorsionCst1, TorsionCst2, TorsionCst3
                                ])
                            if BBSC:
                                BackboneSidechainCst.extend([
                                    DistanceCst, AngleCst1, AngleCst2,
                                    TorsionCst1, TorsionCst2, TorsionCst3
                                ])
                            if SCSC:
                                SidechainSidechainCst.extend([
                                    DistanceCst, AngleCst1, AngleCst2,
                                    TorsionCst1, TorsionCst2, TorsionCst3
                                ])

                        # else:
                        #   print 'No hydrogen!'
                        #   sys.exit()

            AllConstraints.extend(Constraints)
            AllBackboneBackboneCst.extend(BackboneBackboneCst)
            AllBackboneSidechainCst.extend(BackboneSidechainCst)
            AllSidechainSidechainCst.extend(SidechainSidechainCst)

    SortedConstraints = (AllBackboneBackboneCst, AllBackboneSidechainCst,
                         AllSidechainSidechainCst)

    return AllConstraints, SortedConstraints
Ejemplo n.º 4
0
def get_pose_constraints(Pose, MaxDist, MinPositionSeperation, SasaRadius, SasaScale, UpstreamGrep, DownstreamGrep, NeedHydrogen=True):
    '''  '''
    # AlexsSasaCalculator is from Alex's interface_fragment_matching 
    # thanks Alex!
    #
    # This is used to give buried polar contacts more weight. Thanks Alex Ford!
    try:
      from interface_fragment_matching.utility.analysis import AtomicSasaCalculator
      # make instace of Alex's sasa calculator
      AlexsSasaCalculator = AtomicSasaCalculator(probe_radius=SasaRadius)
      ResidueAtomSasa = AlexsSasaCalculator.calculate_per_atom_sasa(Pose)    
    except ImportError:
      ' Error: SASA weighting of contacts requires interface_fragment_matching from Alex Ford '

    # for making full atom kd tree
    ResAtmCoordLists = []
    # for translating from kd tree index to ( residue, atom ) coord
    ResAtmRecordLists = []

    # loop through all residue numbers
    for Res in range(1, Pose.n_residue() + 1):
      # remade for each residue
      AtmRecordList = []
      AtmCoordList = []
      # loop through residue's atom numbers
      for Atm in range(1, Pose.residue(Res).natoms() + 1):
        # add (residue, atom) coord to residue's list
        AtmRecordList.append((Res, Atm))
        # add atom xyz coord to residue's list
        AtmCoordList.append( np.array(list(Pose.residue(Res).atom(Atm).xyz())) )
      
      # add residue's lists to respective global lists
      ResAtmCoordLists.extend(AtmCoordList)
      ResAtmRecordLists.extend(AtmRecordList)

    ResidueAtomArray = np.array( ResAtmCoordLists )
    ResidueAtomKDTree = spatial.KDTree( ResidueAtomArray )

    ResidueAtomNeighbors = ResidueAtomKDTree.query_ball_point( ResidueAtomArray, MaxDist )
    # ResidueAtomNearNeighbors = ResidueAtomKDTree.query_ball_point( ResidueAtomArray, 2.0 )
    ResidueAtomHydrogens = ResidueAtomKDTree.query_ball_point( ResidueAtomArray, 1.1 )

    # holds constraints before printing
    AllConstraints = [] 
    # holds sorted cst
    AllBackboneBackboneCst = []
    AllBackboneSidechainCst = []
    AllSidechainSidechainCst = []

    # All contacts are from upstream to downstream residues to avoid double counting
    Upstream = []
    for UpIndex, UpXyzCoords in enumerate(ResAtmCoordLists):
      UpRes, UpAtm = ResAtmRecordLists[UpIndex]

      # # loop through residues storing info on oxygens
      # for UpRes in range( 1, Pose.n_residue() + 1 ):
      #   # loop through atoms
      #   for UpAtm in range( 1, Pose.residue(UpRes).natoms() + 1 ):
      UpName = Pose.residue(UpRes).atom_name(UpAtm).replace(' ', '')

      # skip virtual residues
      if Pose.residue(UpRes).is_virtual(UpAtm):
        continue

      #                                this guy 
      #                                 /
      # checks upstream name           V
      if re.match(UpstreamGrep, UpName ): 
        # print '\n'*2
        # print 'UpRes, UpName', UpRes, UpName

        # get neighbors of upstream residues
        NeighborsOfUpstream = ResidueAtomNeighbors[UpIndex]
        
        # prep for loop
        Downstreams = []

        Constraints = []
        BackboneBackboneCst = []
        BackboneSidechainCst = []
        SidechainSidechainCst = []

        # ArbitrayOrderOfAtomNames = {}
        for DownIndex in NeighborsOfUpstream:
          # name presumes downstream, checks with if imediately below
          DownRes, DownAtm = ResAtmRecordLists[DownIndex]

          # checks that downstream residue is dowstream of upstream and passes min primary sequence spacing
          if DownRes - UpRes >= MinPositionSeperation:
            DownName = Pose.residue(DownRes).atom_name(DownAtm).replace(' ', '')
            
            # skip if same atom
            if UpRes == DownRes:
              if UpName == DownName:
                continue

            # skip virtual residues
            if Pose.residue(DownRes).is_virtual(DownAtm):
              continue

            # checks downstream name
            if re.match( DownstreamGrep, DownName ):
              # print 'DownRes, DownName', DownRes, DownName

              PotentialUpstreamHydrogens = ResidueAtomHydrogens[UpIndex]
              UpstreamHydrogens = []
              # print 'PotentialUpstreamHydrogens', PotentialUpstreamHydrogens
              for UpH_I in PotentialUpstreamHydrogens:
                UpH_Res, UpH_Atm = ResAtmRecordLists[UpH_I]
                UpH_Name  = Pose.residue(UpH_Res).atom_name(UpH_Atm).replace(' ', '')
                # print 'UpH_Name', UpH_Name
                if 'H' in UpH_Name:
                  UpstreamHydrogens.append((UpH_Res, UpH_Atm, UpH_Name))
                # print 'UpstreamHydrogens', UpstreamHydrogens

              PotentialDownstreamHydrogens = ResidueAtomHydrogens[DownIndex]
              DownstreamHydrogens = []
              # print 'PotentialDownstreamHydrogens', PotentialDownstreamHydrogens
              for DownH_I in PotentialDownstreamHydrogens:
                DownH_Res, DownH_Atm = ResAtmRecordLists[DownH_I]
                DownH_Name = Pose.residue(DownH_Res).atom_name(DownH_Atm).replace(' ', '')
                # print 'DownH_Name', DownH_Name
                if 'H' in DownH_Name:
                  DownstreamHydrogens.append((DownH_Res, DownH_Atm, DownH_Name))
                # print 'DownstreamHydrogens', DownstreamHydrogens

              # check their is at least one hydrogen in system before adding constraint
              if len(UpstreamHydrogens) or len(DownstreamHydrogens) or NeedHydrogen == False:

                # these trys / excepts seperate 
                # backbone-backbone from 
                # backbone-sidechain from
                # sidechain-sidechain interactions
                # 
                # in future maybe sort into seperate lists, shouldn't rely on ResidueAtomSasa to know what is in backbone
                try:
                  UpstreamSasa = ResidueAtomSasa[UpRes][UpName]
                  DownstreamSasa = ResidueAtomSasa[DownRes][DownName]
                  AverageSasa = np.mean([UpstreamSasa, DownstreamSasa])        
                  BBBB = 1
                  BBSC = SCSC = 0
                except KeyError:                
                  # These lines handle backbone to sidechain interactions
                  # set weight equal to the most buried 
                  try:
                    UpstreamSasa = ResidueAtomSasa[UpRes][UpName]
                    AverageSasa = SasaScale.FloorSasa
                    BBSC = 1
                    BBBB = SCSC = 0
                  except KeyError:
                    try:
                      DownstreamSasa = ResidueAtomSasa[DownRes][DownName]
                      AverageSasa = SasaScale.FloorSasa 
                      BBSC = 1
                      BBBB = SCSC = 0            
                    
                    # set weight of side chain side chain equal to the most buried             
                    except KeyError:
                      AverageSasa = SasaScale.CeilingSasa 
                      SCSC = 1
                      BBSC = BBBB = 0

                # use instance of sasa_scale to calculate weight based on avg sasa of N and O
                SasaBasedWeight = SasaScale.weigh(AverageSasa)
                # print 
                # print 'AverageSasa', AverageSasa
                # print 'SasaBasedWeight', SasaBasedWeight

                # print 'found downstream neighbor %s'%DownName
                DownXyzCoords = np.array( list(Pose.residue(DownRes).atom(DownAtm).xyz()) )
                # print 'DownRes, DownName', DownRes, DownName
                # print 'DownXyzCoords', DownXyzCoords

                # ## Get neighbors for angles and torsions to use with AtomPairs

                SelectUpNeighbors = []
                # iterates through upstream atom neighbors for references for angle
                for UpNeighborIndex in NeighborsOfUpstream:
                  UpNeighborRes, UpNeighborAtm = ResAtmRecordLists[UpNeighborIndex]
                  UpNeighborName = Pose.residue(UpNeighborRes).atom_name(UpNeighborAtm).replace(' ', '')

                  # keep looking if neighbor is hyrdogen
                  if 'H' in UpNeighborName:
                    continue                

                  # skip virtual residues
                  if Pose.residue(UpNeighborRes).is_virtual(UpNeighborAtm):
                    continue

                  # keep looking if neighbor is self
                  if UpNeighborName == UpName and UpNeighborRes == UpRes:
                    continue
                  # keep looking if neighbor is downstream residue again
                  if UpNeighborName == DownName and UpNeighborRes == DownRes:
                    continue
                  UpNeighborCoords = ResAtmCoordLists[UpNeighborIndex]
                  DistanceToNeighbor = solenoid_tools.vector_magnitude( UpXyzCoords - UpNeighborCoords )
                  SelectUpNeighbors.append( (DistanceToNeighbor, UpNeighborName, UpNeighborRes, UpNeighborCoords) )

                # sort by distance to atom, nearest first
                SelectUpNeighbors.sort()                
                UpNeighbor1Tuple = SelectUpNeighbors[0]
                UpNeighbor2Tuple = SelectUpNeighbors[1]
                # print '\n'*2
                # print 'UpRes, UpName', UpRes, UpName
                # print 'UpstreamHydrogens', UpstreamHydrogens
                # print 'SelectUpNeighbors', SelectUpNeighbors

                 # get neighbors of upstream residues
                NeighborsOfDownstream = ResidueAtomNeighbors[DownIndex]
                SelectDownNeighbors = []
                # iterates through upstream atom neighbors for references for angle
                for DownNeighborIndex in NeighborsOfDownstream:
                  DownNeighborRes, DownNeighborAtm = ResAtmRecordLists[DownNeighborIndex]
                  DownNeighborName = Pose.residue(DownNeighborRes).atom_name(DownNeighborAtm).replace(' ', '')

                  # keep looking if neighbor is hyrdogen
                  if 'H' in DownNeighborName:
                    continue                

                  # skip virtual residues
                  if Pose.residue(DownNeighborRes).is_virtual(DownNeighborAtm):
                    continue

                  # keep looking if neighbor is self
                  if DownNeighborName == DownName and DownNeighborRes == DownRes:
                    continue
                  # keep looking if neighbor is upstream residue
                  if DownNeighborName == UpName and DownNeighborRes == UpRes:
                    continue

                  DownNeighborCoords = ResAtmCoordLists[DownNeighborIndex]
                  DistanceToNeighbor = solenoid_tools.vector_magnitude( DownXyzCoords - DownNeighborCoords )
                  SelectDownNeighbors.append( (DistanceToNeighbor, DownNeighborName, DownNeighborRes, DownNeighborCoords) )

                # sort by distance to atom, nearest first
                SelectDownNeighbors.sort()
                DownNeighbor1Tuple = SelectDownNeighbors[0]
                DownNeighbor2Tuple = SelectDownNeighbors[1]
                # print 'DownRes, DownName', DownRes, DownName
                # print 'DownstreamHydrogens', DownstreamHydrogens
                # print 'SelectDownNeighbors', SelectDownNeighbors

                Distance = solenoid_tools.vector_magnitude(DownXyzCoords - UpXyzCoords)
                
                DistanceCst = 'AtomPair %s %d %s %d SCALARWEIGHTEDFUNC %f HARMONIC %.2f 1.0' %( UpName, UpRes, DownName, DownRes, SasaBasedWeight, Distance )

                # Use Biopython for angle and dihedral calculations
                # here 'Vec' means PDB.Vector of atom's xyz coord
                UpstreamVec = PDB.Vector(UpXyzCoords)
                DownstreamVec = PDB.Vector(DownXyzCoords)
                
                UpNeighbor1Vec = PDB.Vector(UpNeighbor1Tuple[3])
                UpNeighbor2Vec = PDB.Vector(UpNeighbor2Tuple[3])
                DownNeighbor1Vec = PDB.Vector(DownNeighbor1Tuple[3])
                DownNeighbor2Vec = PDB.Vector(DownNeighbor2Tuple[3])

                Angle1 = PDB.calc_angle(UpNeighbor1Vec, UpstreamVec, DownstreamVec)
                AngleCst1 = 'Angle %s %d %s %d %s %d SCALARWEIGHTEDFUNC %f CIRCULARHARMONIC %.2f 0.5' %( UpNeighbor1Tuple[1], UpNeighbor1Tuple[2], UpName, UpRes, DownName, DownRes, SasaBasedWeight, Angle1 )
                Angle2 = PDB.calc_angle(UpstreamVec, DownstreamVec, DownNeighbor1Vec)
                AngleCst2 = 'Angle %s %d %s %d %s %d SCALARWEIGHTEDFUNC %f CIRCULARHARMONIC %.2f 0.5' %( UpName, UpRes, DownName, DownRes, DownNeighbor1Tuple[1], DownNeighbor1Tuple[2], SasaBasedWeight, Angle2 )

                Torsion1 = PDB.calc_dihedral(UpNeighbor2Vec, UpNeighbor1Vec, UpstreamVec, DownstreamVec)
                TorsionCst1 = 'Dihedral %s %d %s %d %s %d %s %d SCALARWEIGHTEDFUNC %f CIRCULARHARMONIC %.2f 0.5' %( UpNeighbor2Tuple[1], UpNeighbor2Tuple[2], UpNeighbor1Tuple[1], UpNeighbor1Tuple[2], UpName, UpRes, DownName, DownRes, SasaBasedWeight, Torsion1 )
                Torsion2 = PDB.calc_dihedral(UpNeighbor1Vec, UpstreamVec, DownstreamVec, DownNeighbor1Vec)
                TorsionCst2 = 'Dihedral %s %d %s %d %s %d %s %d SCALARWEIGHTEDFUNC %f CIRCULARHARMONIC %.2f 0.5' %( UpNeighbor1Tuple[1], UpNeighbor1Tuple[2], UpName, UpRes, DownName, DownRes, DownNeighbor1Tuple[1], DownNeighbor1Tuple[2], SasaBasedWeight, Torsion2 )
                Torsion3 = PDB.calc_dihedral(UpstreamVec, DownstreamVec, DownNeighbor1Vec, DownNeighbor2Vec)
                TorsionCst3 = 'Dihedral %s %d %s %d %s %d %s %d SCALARWEIGHTEDFUNC %f CIRCULARHARMONIC %.2f 0.5' %( UpName, UpRes, DownName, DownRes, DownNeighbor1Tuple[1], DownNeighbor1Tuple[2], DownNeighbor2Tuple[1], DownNeighbor2Tuple[2], SasaBasedWeight, Torsion3 )

                # adds constraint to running lists of constraints
                Constraints.extend( [DistanceCst, AngleCst1, AngleCst2, TorsionCst1, TorsionCst2, TorsionCst3] )
                if BBBB: BackboneBackboneCst.extend( [DistanceCst, AngleCst1, AngleCst2, TorsionCst1, TorsionCst2, TorsionCst3] )
                if BBSC: BackboneSidechainCst.extend( [DistanceCst, AngleCst1, AngleCst2, TorsionCst1, TorsionCst2, TorsionCst3] )
                if SCSC: SidechainSidechainCst.extend( [DistanceCst, AngleCst1, AngleCst2, TorsionCst1, TorsionCst2, TorsionCst3] )

              # else:
              #   print 'No hydrogen!'
              #   sys.exit()

        AllConstraints.extend(Constraints)
        AllBackboneBackboneCst.extend(BackboneBackboneCst)
        AllBackboneSidechainCst.extend(BackboneSidechainCst)
        AllSidechainSidechainCst.extend(SidechainSidechainCst)

    SortedConstraints = (AllBackboneBackboneCst, AllBackboneSidechainCst, AllSidechainSidechainCst)

    return AllConstraints, SortedConstraints