diff --git a/cadnano_segments.py b/cadnano_segments.py
index e9d0f578bce5418077723d36087e8270ed754316..682b8b67d1a24b1058305fd6768396a441b8dccf 100644
--- a/cadnano_segments.py
+++ b/cadnano_segments.py
@@ -344,167 +344,6 @@ class cadnano_part(SegmentModel):
                 endList.append(lastStrand[1])
         return endLists
 
-    def combineRegionLists(self,loHi1,loHi2,intersect=False):
-
-        """Combines two lists of (lo,hi) pairs specifying integer
-        regions a single list of regions.  """
-
-        ## Validate input
-        for l in (loHi1,loHi2):
-            ## Assert each region in lists is sorted
-            for pair in l:
-                assert(len(pair) == 2)
-                assert(pair[0] <= pair[1])
-
-        if len(loHi1) == 0:
-            if intersect:
-                return []
-            else:
-                return loHi2
-        if len(loHi2) == 0:
-            if intersect:
-                return []
-            else:
-                return loHi1
-
-        ## Break input into lists of compact regions
-        compactRegions1,compactRegions2 = [[],[]]
-        for compactRegions,loHi in zip(
-                [compactRegions1,compactRegions2],
-                [loHi1,loHi2]):
-            tmp = []
-            lastHi = loHi[0][0]-1
-            for lo,hi in loHi:
-                if lo-1 != lastHi:
-                    compactRegions.append(tmp)
-                    tmp = []
-                tmp.append((lo,hi))
-                lastHi = hi
-            if len(tmp) > 0:
-                compactRegions.append(tmp)
-
-        ## Build result
-        result = []
-        region = []
-        i,j = [0,0]
-        compactRegions1.append([[1e10]])
-        compactRegions2.append([[1e10]])
-        while i < len(compactRegions1)-1 or j < len(compactRegions2)-1:
-            cr1 = compactRegions1[i]
-            cr2 = compactRegions2[j]
-
-            ## initialize region
-            if len(region) == 0:
-                if cr1[0][0] <= cr2[0][0]:
-                    region = cr1
-                    i += 1
-                    continue
-                else:
-                    region = cr2
-                    j += 1
-                    continue
-
-            if region[-1][-1] >= cr1[0][0]:
-                region = self.combineCompactRegionLists(region, cr1, intersect=False)
-                i+=1
-            elif region[-1][-1] >= cr2[0][0]:
-                region = self.combineCompactRegionLists(region, cr2, intersect=False)
-                j+=1
-            else:
-                result.extend(region)
-                region = []
-
-        assert( len(region) > 0 )
-        result.extend(region)
-        result = sorted(result)
-
-        # print("loHi1:",loHi1)
-        # print("loHi2:",loHi2)
-        # print(result,"\n")
-
-        if intersect:
-            lo = max( [loHi1[0][0], loHi2[0][0]] )
-            hi = min( [loHi1[-1][1], loHi2[-1][1]] )
-            result = [r for r in result if r[0] >= lo and r[1] <= hi]
-
-        return result
-
-    def combineCompactRegionLists(self,loHi1,loHi2,intersect=False):
-        """Combines two lists of (lo,hi) pairs specifying regions within a
-        compact integer set into a single list of regions.
-
-        examples:
-        loHi1 = [[0,4],[5,7]]
-        loHi2 = [[2,4],[5,9]]
-        out = [(0, 1), (2, 4), (5, 7), (8, 9)]
-
-        loHi1 = [[0,3],[5,7]]
-        loHi2 = [[2,4],[5,9]]
-        out = [(0, 1), (2, 3), (4, 4), (5, 7), (8, 9)]
-        """
-
-        ## Validate input
-        for l in (loHi1,loHi2):
-            ## Assert each region in lists is sorted
-            for pair in l:
-                assert(len(pair) == 2)
-                assert(pair[0] <= pair[1])
-            ## Assert lists are compact
-            for pair1,pair2 in zip(l[::2],l[1::2]):
-                assert(pair1[1]+1 == pair2[0])
-
-        if len(loHi1) == 0:
-            if intersect:
-                return []
-            else:
-                return loHi2
-        if len(loHi2) == 0:
-            if intersect:
-                return []
-            else:
-                return loHi1
-
-        ## Find the ends of the region
-        lo = min( [loHi1[0][0], loHi2[0][0]] )
-        hi = max( [loHi1[-1][1], loHi2[-1][1]] )
-
-        ## Make a list of indices where each region will be split
-        splitAfter = []
-        for l,h in loHi2:
-            if l != lo:
-                splitAfter.append(l-1)
-            if h != hi:
-                splitAfter.append(h)
-
-        for l,h in loHi1:
-            if l != lo:
-                splitAfter.append(l-1)
-            if h != hi:
-                splitAfter.append(h)
-        splitAfter = sorted(list(set(splitAfter)))
-
-        # print("splitAfter:",splitAfter)
-
-        split=[]
-        last = -2
-        for s in splitAfter:
-            split.append(s)
-            last = s
-
-        # print("split:",split)
-        returnList = [(i+1,j) if i != j else (i,j) for i,j in zip([lo-1]+split,split+[hi])]
-
-        if intersect:
-            lo = max( [loHi1[0][0], loHi2[0][0]] )
-            hi = min( [loHi1[-1][1], loHi2[-1][1]] )
-            returnList = [r for r in returnList if r[0] >= lo and r[1] <= hi]
-
-        # print("loHi1:",loHi1)
-        # print("loHi2:",loHi2)
-        # print(returnList,"\n")
-        return returnList
-
-
     def _helix_strands_to_segment_ranges(self, helix_strands):
         """Utility method to convert cadnano strand lists into list of
         indices of terminal points"""
@@ -595,24 +434,40 @@ class cadnano_part(SegmentModel):
                         ## TODO: handle nicks that are at intrahelical connections(?)
                         zmid1 = int(0.5*(r1[0]+r1[1]))
                         zmid2 = int(0.5*(r2[0]+r2[1]))
+                        if seg1.name == "19-3" or seg2.name == "19-3":
+                            import pdb
+                            pdb.set_trace()
+                        
+                # if zMid in strandOccupancies[0] and zMid in strandOccupancies[1]:
+                #     seg = DoubleStrandedSegment(**kwargs,**posargs1)
+                # elif zMid in strandOccupancies[0]:
+                #     seg = SingleStrandedSegment(**kwargs,**posargs1)
+                # elif zMid in strandOccupancies[1]:
+                #     seg = SingleStrandedSegment(**kwargs,**posargs2)
+
                         ## TODO: validate
                         if zmid1 in occ[0] and zmid2 in occ[0]:
                             seg1.connect_end3(seg2.start5)
 
                         if zmid1 in occ[1] and zmid2 in occ[1]:
                             if zmid1 in occ[0]:
-                                seg2.connect_end3(seg1.end5)
+                                end = seg1.end5
+                            else:
+                                end = seg1.start5
+                            if zmid2 in occ[0]:
+                                seg2.connect_start3(end)
                             else:
                                 seg2.connect_end3(seg1.start5)
                         
     def _add_crossovers(self):
         for h1,f1,z1,h2,f2,z2 in self.xover_list:
-            if (h1 == 52 or h2 == 52) and z1 == 221:
-                import pdb
-                pdb.set_trace()
+            # if (h1 == 52 or h2 == 52) and z1 == 221:
+            #     import pdb
+            #     pdb.set_trace()
             seg1, nt1 = self._get_segment_nucleotide(h1,z1)
             seg2, nt2 = self._get_segment_nucleotide(h2,z2)
             ## TODO: use different types of crossovers
+            ## fwd?
             seg1.add_crossover(nt1,seg2,nt2,[f1,f2])
 
     def _add_prime_ends(self):
diff --git a/segmentmodel.py b/segmentmodel.py
index 58a2767c93ee2bf0fcb922c5be8b2708adc4fa3f..ca71dae9033434a79e4292286e7202713b127b09 100644
--- a/segmentmodel.py
+++ b/segmentmodel.py
@@ -1,3 +1,4 @@
+import pdb
 import numpy as np
 import random
 from arbdmodel import PointParticle, ParticleType, Group, ArbdModel
@@ -36,12 +37,15 @@ TODO:
  - add unit test of helices connected to themselves
 """
 
+class ParticleNotConnectedError(Exception):
+    pass
+
 class Location():
     """ Site for connection within an object """
     def __init__(self, container, address, type_, on_fwd_strand = True):
         ## TODO: remove cyclic references(?)
         self.container = container
-        self.address = address  # represents position along contour length in segments
+        self.address = address  # represents position along contour length in segment
         # assert( type_ in ("end3","end5") ) # TODO remove or make conditional
         self.on_fwd_strand = on_fwd_strand
         self.type_ = type_
@@ -69,8 +73,6 @@ class Location():
             on_fwd = "on_fwd_strand"
         else:
             on_fwd = "on_rev_strand"
-        # return "<Location in {} at contour {} {} with connection {}>".format( self.container.name, self.address, self.on_fwd_strand, self.connection )
-        # return "<Location {} in {} at contour {} {} with connection {}>".format( self.type_, self.container.name, self.address, on_fwd, self.connection )
         return "<Location {}.{}[{:.2f},{:d}]>".format( self.container.name, self.type_, self.address, self.on_fwd_strand)
         
 class Connection():
@@ -89,6 +91,11 @@ class Connection():
             return self.A
         else:
             raise Exception("OutOfBoundsError")
+
+    def __repr__(self):
+        return "<Connection {}--{}--{}]>".format( self.A, self.type_, self.B )
+        
+
         
 # class ConnectableElement(Transformable):
 class ConnectableElement():
@@ -120,7 +127,7 @@ class ConnectableElement():
                 if l.on_fwd_strand == on_fwd_strand and l.connection is None:
                     assert(loc is None)
                     loc = l
-            assert( loc is not None )
+            # assert( loc is not None )
         else:
             for l in self.locations:
                 if l.address == address and l.on_fwd_strand == on_fwd_strand:
@@ -173,31 +180,7 @@ class SegmentParticle(PointParticle):
         PointParticle.__init__(self, type_, position, name=name, segname=segname, **kwargs)
         self.intrahelical_neighbors = []
         self.other_neighbors = []
-
-    # def get_contour_position(self,seg):
-    #     assert( isinstance(seg,Segment) )
-    #     if seg == self.parent:
-    #         return self.contour_position
-    #     else:
-    #         ## TODO replace with something more elegant
-    #         for c,A,B in self.parent.get_connections_and_locations():
-    #             if A.particle is self and B.container is seg:
-    #                 nt = np.abs( (self.contour_position - A.address)*(A.container.num_nts-1) )
-    #                 if B.address < 0.5:
-    #                     return B.address-nt/(seg.num_nts-1)
-    #                 else:
-    #                     return B.address+nt/(seg.num_nts-1)
-    #         ## ERROR
-    #         print("")
-    #         for c,A,B in self.parent.get_connections_and_locations():
-    #             print("  ",c.type_)
-    #             print(A,B)
-    #             print(A.particle,self)
-    #             print(B.container,seg)
-    #         print("")
-    #         import pdb
-    #         pdb.set_trace()
-    #         raise Exception("Did not find location for particle {} in Segment {}".format(self,seg))
+        self.locations = []
 
     def get_intrahelical_above(self):
         """ Returns bead directly above self """
@@ -212,45 +195,99 @@ class SegmentParticle(PointParticle):
         for b in self.intrahelical_neighbors:
             if b.get_contour_position(self.parent) < self.contour_position:
                 return b
-        
 
-    def get_nt_position(self,seg):
-        if seg == self.parent:
-            return seg.contour_to_nt_pos(self.contour_position)
+    def _neighbor_should_be_added(self,b):
+        c1 = self.contour_position
+        c2 = b.get_contour_position(self.parent)
+        if c2 < c1:
+            b0 = self.get_intrahelical_below()
         else:
-            cl = [e for e in self.parent.get_connections_and_locations() if e[2].container is seg]
-            dc = [(self.contour_position - A.address)**2 for c,A,B in cl]
+            b0 = self.get_intrahelical_above()
+
+        if b0 is not None:            
+            c0 = b0.get_contour_position(self.parent)
+            if np.abs(c2-c1) < np.abs(c0-c1):
+                ## remove b0
+                self.intrahelical_neighbors.remove(b0)
+                b0.intrahelical_neighbors.remove(self)
+                return True
+            else:
+                return False
+        return True
+        
+    def make_intrahelical_neighbor(self,b):
+        add1 = self._neighbor_should_be_added(b)
+        add2 = b._neighbor_should_be_added(self)
+        if add1 and add2:
+            assert(len(b.intrahelical_neighbors) <= 1)
+            assert(len(self.intrahelical_neighbors) <= 1)
+            self.intrahelical_neighbors.append(b)
+            b.intrahelical_neighbors.append(self)
 
-            if len(dc) == 0:
-                pdb.set_trace()
+        
+    # def get_nt_position(self,seg):
+    #     if seg == self.parent:
+    #         return seg.contour_to_nt_pos(self.contour_position)
+    #     else:
+    #         cl = [e for e in self.parent.get_connections_and_locations() if e[2].container is seg]
 
-            i = np.argmin(dc)
-            c,A,B = cl[i]
-            ## TODO: generalize, removing np.abs and conditional 
-            delta_nt = np.abs( A.container.contour_to_nt_pos(self.contour_position - A.address) )
-            B_nt_pos = seg.contour_to_nt_pos(B.address)
-            if B.address < 0.5:
-                return B_nt_pos-delta_nt
-            else:
-                return B_nt_pos+delta_nt
+    #         dc = [(self.contour_position - A.address)**2 for c,A,B in cl]
+
+    #         if len(dc) == 0:
+    #             import pdb
+    #             pdb.set_trace()
 
-    def get_contour_position_old(self,seg):
+    #         i = np.argmin(dc)
+    #         c,A,B = cl[i]
+    #         ## TODO: generalize, removing np.abs and conditional 
+    #         delta_nt = np.abs( A.container.contour_to_nt_pos(self.contour_position - A.address) )
+    #         B_nt_pos = seg.contour_to_nt_pos(B.address)
+    #         if B.address < 0.5:
+    #             return B_nt_pos-delta_nt
+    #         else:
+    #             return B_nt_pos+delta_nt
+
+    def get_nt_position(self,seg):
         if seg == self.parent:
-            return self.contour_position
+            return seg.contour_to_nt_pos(self.contour_position)
         else:
-            cl = [e for e in self.parent.get_connections_and_locations() in B.container is seg]
-            dc = [(self.contour_position - A.address)**2 for c,A,B in e]
 
-            if len(dc) == 0:
-                pdb.set_trace()
+            def get_nt_pos(contour1, seg1, seg2):
+                cl = [e for e in seg1.get_connections_and_locations() if e[2].container is seg2]
+                dc = [(contour1 - A.address)**2 for c,A,B in cl]
+                if len(dc) == 0: return None
 
-            i = np.argmin(dc)
+                i = np.argmin(dc)
+                c,A,B = cl[i]
+
+                ## TODO: generalize, removing np.abs and conditional 
+                delta_nt = np.abs( seg1.contour_to_nt_pos(contour1 - A.address) )
+                B_nt_pos = seg2.contour_to_nt_pos(B.address)
+                if B.address < 0.5:
+                    return B_nt_pos-delta_nt
+                else:
+                    return B_nt_pos+delta_nt
+                
+            pos = get_nt_pos(self.contour_position, self.parent, seg)
+            if pos is None:
+                ## Particle is not directly connected
+                visited_segs = set(seg)
+                positions = []
+                for l in self.locations:
+                    if l.container == self.parent: continue
+                    pos0 = get_nt_pos(self.contour_position, self.parent, l.container)
+                    assert(pos0 is not None)
+                    pos0 = l.container.nt_pos_to_contour(pos0)
+                    pos = get_nt_pos( pos0, l.container, seg )
+                    if pos is not None:
+                        positions.append( pos )
+                assert( len(positions) > 0 )
+                if len(positions) > 1:
+                    import pdb
+                    pdb.set_trace()
+                pos = positions[0]
+            return pos
 
-            nt = np.abs( (self.contour_position - A.address)*(A.container.num_nts-1) )
-            if B.address < 0.5:
-                return seg.nt_pos_to_contour(B.address-nt)
-            else:
-                return seg.nt_pos_to_contour(B.address+nt)
 
     def get_contour_position(self,seg):
         if seg == self.parent:
@@ -323,18 +360,14 @@ class Segment(ConnectableElement, Group):
             loc.particle = None
 
     def contour_to_nt_pos(self, contour_pos, round_nt=False):
-        nt = contour_pos*(self.num_nts-1)
+        nt = contour_pos*(self.num_nts) - 0.5
         if round_nt:
-            assert( (np.around(nt) - nt)**2 < 1e-3 )
+            assert( np.isclose(np.around(nt),nt) )
             nt = np.around(nt)
         return nt
 
     def nt_pos_to_contour(self,nt_pos):
-        if self.num_nts == 1:
-            assert(nt_pos == 0)
-            return 0
-        else:
-            return nt_pos/(self.num_nts-1)
+        return (nt_pos+0.5)/(self.num_nts)
 
     def contour_to_position(self,s):
         p = interpolate.splev( s, self.position_spline_params )
@@ -482,14 +515,24 @@ class Segment(ConnectableElement, Group):
         move_at_least = 0
 
         ## Iterate through locations
+        # locations = sorted(self.locations, key=lambda l:(l.address,not l.on_fwd_strand), reverse=(not is_fwd))
         locations = sorted(self.locations, key=lambda l:(l.address,not l.on_fwd_strand), reverse=(not is_fwd))
         # print(locations)
 
         for l in locations:
-            pos = self.contour_to_nt_pos(l.address, round_nt=True)
+            # TODOTODO probably okay
+            if l.address == 0:
+                pos = 0.0
+            elif l.address == 1:
+                pos = self.num_nts-1
+            else:
+                pos = self.contour_to_nt_pos(l.address, round_nt=True)
 
             ## DEBUG
 
+            # import pdb
+            # pdb.set_trace()
+
             ## Skip locations encountered before our strand
             # tol = 0.1
             # if is_fwd:
@@ -514,7 +557,15 @@ class Segment(ConnectableElement, Group):
             if l.on_fwd_strand == is_fwd:
                 print("  passing through",l)
                 print("from {}, connection {} to {}".format(nt_pos,l,B))
-                Bpos = B.container.contour_to_nt_pos(B.address, round_nt=True)
+                try:
+                    Bpos = B.container.contour_to_nt_pos(B.address, round_nt=True)
+                except:
+                    if B.address == 0:
+                        Bpos = 0
+                    elif B.address == 1:
+                        Bpos = B.container.num_nts-1
+                    else:
+                        raise
                 return pos, B.container, Bpos, B.on_fwd_strand, 0.5
                 
             ## Stop at other strand crossovers so basepairs line up
@@ -530,66 +581,6 @@ class Segment(ConnectableElement, Group):
         ## Made it to the end of the segment without finding a connection
         return 1*is_fwd, None, None, None
 
-
-    def get_end_of_strand_old(self, contour_pos, is_fwd):
-        """ Walks through locations, checking for crossovers """
-
-        ## Iterate through locations
-        # for l in self.locations:
-        def loc_iter():
-            locations = sorted(self.locations, key=lambda l:(l.address,not l.on_fwd_strand), reverse=(not is_fwd))
-            # if is_fwd:
-            for l in locations:
-                yield l
-            # else:
-            #     for l in locations[::-1]:
-            #         yield l
-            
-        for l in loc_iter():
-            # if l.particle is None:
-            #     pos = l.address
-            # else:
-            #     pos = l.particle.get_contour_position()          
-            pos = l.address
-
-            ## DEBUG
-            # if self.name == "1-0" and is_fwd == False:
-            #     import pdb
-            #     pdb.set_trace()
-
-            ## Skip locations encountered before our strand
-            if is_fwd:
-                if pos <= contour_pos: continue
-            elif pos >= contour_pos: continue
-
-            # print("  ?",l)
-            
-            ## Stop if we found the 3prime end
-            if l.on_fwd_strand == is_fwd and l.type_ == "3prime":
-                return pos, None, None, None
-
-            ## Check location connections
-            c = l.connection
-            if c is None: continue
-            B = c.other(l)            
-
-            ## Found a location on the same strand?
-            if l.on_fwd_strand == is_fwd:
-                # print("  passing through",l)
-                # print("from {}, connection {} to {}".format(contour_pos,l,B))
-                return pos, B.container, B.address, B.on_fwd_strand
-                
-            ## Stop at other strand crossovers so basepairs line up
-            elif c.type_ == "crossover":
-                # print("  pausing at",l)
-                # print("pausing at {}".format(l))
-                return pos, l.container, pos, is_fwd
-
-        raise Exception("Shouldn't be here")
-        # print("Shouldn't be here")
-        ## Made it to the end of the segment without finding a connection
-        return 1*is_fwd, None, None, None
-        
     def get_nearest_bead(self, contour_position):
         if len(self.beads) < 1: return None
         cs = np.array([b.contour_position for b in self.beads]) # TODO: cache
@@ -674,17 +665,19 @@ class Segment(ConnectableElement, Group):
         for b in existing_beads:
             assert(b.parent is not None)
 
+        # if self.name == "1-1":
+        #     import pdb
+        #     pdb.set_trace()
+
         ## Add ends if they don't exist yet
         ## TODOTODO: test 1 nt segments?
-        if len(existing_beads) == 0 or existing_beads[0].get_contour_position(self) > 0:
-            if len(existing_beads) > 0:            
-                assert(existing_beads[0].get_nt_position(self) >= 0.5)
-
+        if len(existing_beads) == 0 or existing_beads[0].get_nt_position(self) > 0.5:
+            # if len(existing_beads) > 0:            
+            #     assert(existing_beads[0].get_nt_position(self) >= 0.5)
             b = self._generate_one_bead(0, 0)
             existing_beads = [b] + existing_beads
-        if existing_beads[-1].get_contour_position(self) < 1:
-            # assert((1-existing_beads[0].get_contour_position(self))*(self.num_nts-1) >= 0.5)
-            assert(self.num_nts-1-existing_beads[0].get_nt_position(self) >= 0.5)
+
+        if existing_beads[-1].get_nt_position(self)-(self.num_nts-1) < -0.5:
             b = self._generate_one_bead(1, 0)
             existing_beads.append(b)
         assert(len(existing_beads) > 1)
@@ -694,11 +687,13 @@ class Segment(ConnectableElement, Group):
         last = None
         for I in range(len(existing_beads)-1):
             eb1,eb2 = [existing_beads[i] for i in (I,I+1)]
-            if eb1 is eb2:
-                pdb.set_trace()
             assert( eb1 is not eb2 )
 
-            # print(" %s working on %d to %d" % (self.name, eb1.position[2], eb2.position[2]))
+            # if np.isclose(eb1.position[2], eb2.position[2]):
+            #     import pdb
+            #     pdb.set_trace()
+
+            print(" %s working on %d to %d" % (self.name, eb1.position[2], eb2.position[2]))
             e_ds = eb2.get_contour_position(self) - eb1.get_contour_position(self)
             num_beads = self._get_num_beads( e_ds, max_basepairs_per_bead, max_nucleotides_per_bead )
             ds = e_ds / (num_beads+1)
@@ -712,26 +707,17 @@ class Segment(ConnectableElement, Group):
 
             s0 = eb1.get_contour_position(self)
             if last is not None:
-                last.intrahelical_neighbors.append(eb1)
-                eb1.intrahelical_neighbors.append(last)
-                assert(len(last.intrahelical_neighbors) <= 2)
-                assert(len(eb1.intrahelical_neighbors) <= 2)
+                last.make_intrahelical_neighbor(eb1)
             last = eb1
             for j in range(num_beads):
                 s = ds*(j+1) + s0
                 b = self._generate_one_bead(s,nts)
 
-                last.intrahelical_neighbors.append(b)
-                b.intrahelical_neighbors.append(last)
-                assert(len(last.intrahelical_neighbors) <= 2)
-                assert(len(b.intrahelical_neighbors) <= 2)
+                last.make_intrahelical_neighbor(b)
                 last = b
                 tmp_children.append(b)
 
-        last.intrahelical_neighbors.append(eb2)
-        eb2.intrahelical_neighbors.append(last)
-        assert(len(last.intrahelical_neighbors) <= 2)
-        assert(len(eb2.intrahelical_neighbors) <= 2)
+        last.make_intrahelical_neighbor(eb2)
 
         if eb2.parent == self:
             tmp_children.append(eb2)
@@ -778,8 +764,8 @@ class DoubleStrandedSegment(Segment):
 
         self.end = self.end3 = Location( self, address=1, type_ = "end3" )
         self.end5 = Location( self, address=1, type_= "end5", on_fwd_strand=False )
-        for l in (self.start5,self.start3,self.end3,self.end5):
-            self.locations.append(l)
+        # for l in (self.start5,self.start3,self.end3,self.end5):
+        #     self.locations.append(l)
 
         ## Set up interpolation for azimuthal angles 
         a = np.array([self.start_position,self.end_position]).T
@@ -810,13 +796,13 @@ class DoubleStrandedSegment(Segment):
             end3 = end3.end3
         self._connect_ends( self.end5, end3, type_, force_connection = force_connection )
 
-    def add_crossover(self, nt, other, other_nt, strands_fwd=[True,False]):
+    def add_crossover(self, nt, other, other_nt, strands_fwd=[True,False], nt_on_5prime=True):
         """ Add a crossover between two helices """
         ## Validate other, nt, other_nt
         ##   TODO
 
         if isinstance(other,SingleStrandedSegment):
-            other.add_crossover(other_nt, self, nt, strands_fwd[::-1])
+            other.add_crossover(other_nt, self, nt, strands_fwd[::-1], not nt_on_5prime)
         else:
 
             ## Create locations, connections and add to segments
@@ -826,12 +812,16 @@ class DoubleStrandedSegment(Segment):
             loc = self.get_location_at(c, strands_fwd[0])
 
             c = other.nt_pos_to_contour(other_nt)
+            # TODOTODO: may need to subtract or add a little depending on 3prime/5prime
             assert(c >= 0 and c <= 1)
             other_loc = other.get_location_at(c, strands_fwd[1])
             self._connect(other, Connection( loc, other_loc, type_="crossover" ))
-            loc.is_3prime_side_of_connection = not strands_fwd[0]
-            other_loc.is_3prime_side_of_connection = not strands_fwd[1]
-            
+            if nt_on_5prime:
+                loc.is_3prime_side_of_connection = False
+                other_loc.is_3prime_side_of_connection = True
+            else:            
+                loc.is_3prime_side_of_connection = True
+                other_loc.is_3prime_side_of_connection = False
 
     ## Real work
     def _connect_ends(self, end1, end2, type_, force_connection):
@@ -842,7 +832,7 @@ class DoubleStrandedSegment(Segment):
             assert( end.type_ in ("end3","end5") )
         assert( end1.type_ != end2.type_ )
         ## Create and add connection
-        if end2.type_ == "end3":
+        if end2.type_ == "end5":
             end1.container._connect( end2.container, Connection( end1, end2, type_=type_ ), in_3prime_direction=True )
         else:
             end2.container._connect( end1.container, Connection( end2, end1, type_=type_ ), in_3prime_direction=True )
@@ -888,58 +878,74 @@ class SingleStrandedSegment(Segment):
 
         self.start = self.start5 = Location( self, address=0, type_= "end5" ) # TODO change type_?
         self.end = self.end3 = Location( self, address=1, type_ = "end3" )
-        for l in (self.start5,self.end3):
-            self.locations.append(l)
+        # for l in (self.start5,self.end3):
+        #     self.locations.append(l)
 
     def connect_end3(self, end5, force_connection=False):
-        self._connect_end( end5,  _5_to_3 = False, force_connection = force_connection )
+        self._connect_end( end5,  _5_to_3 = True, force_connection = force_connection )
 
     def connect_5end(self, end3, force_connection=False): # TODO: change name or possibly deprecate
-        self._connect_end( end3,  _5_to_3 = True, force_connection = force_connection )
+        self._connect_end( end3,  _5_to_3 = False, force_connection = force_connection )
 
     def _connect_end(self, other, _5_to_3, force_connection):
         assert( isinstance(other, Location) )
         if _5_to_3 == True:
-            my_end = self.end5
-            assert( other.type_ == "end3" )
+            my_end = self.end3
+            # assert( other.type_ == "end5" )
+            if (other.type_ is not "end5"):
+                print("Warning: code does not prevent connecting 3prime to 3prime, etc")
             conn = Connection( my_end, other, type_="intrahelical" )
             self._connect( other.container, conn, in_3prime_direction=True )
         else:
-            my_end = self.end3
-            assert( other.type_ == "end5" )
+            my_end = self.end5
+            # assert( other.type_ == "end3" )
+            if (other.type_ is not "end3"):
+                print("Warning: code does not prevent connecting 3prime to 3prime, etc")
             conn = Connection( other, my_end, type_="intrahelical" )
             other.container._connect( self, conn, in_3prime_direction=True )
 
-    def add_crossover(self, nt, other, other_nt, strands_fwd=[True,False]):
+    def add_crossover(self, nt, other, other_nt, strands_fwd=[True,False], nt_on_5prime=True):
         """ Add a crossover between two helices """
         ## Validate other, nt, other_nt
         ##   TODO
        
         ## TODO: fix direction
 
-        c1 = self.nt_pos_to_contour(nt)
-        ## Ensure connections occur at ends, otherwise the structure doesn't make sense
-        assert(np.isclose(c1,0) or np.isclose(c1,1))
+        # c1 = self.nt_pos_to_contour(nt)
+        # # TODOTODO
+        # ## Ensure connections occur at ends, otherwise the structure doesn't make sense
+        # # assert(np.isclose(c1,0) or np.isclose(c1,1))
+        # assert(np.isclose(nt,0) or np.isclose(nt,self.num_nts-1))
+        if nt == 0:
+            c1 = 0
+        elif nt == self.num_nts-1:
+            c1 = 1
+        else:
+            raise Exception("Crossovers can only be at the ends of an ssDNA segment")
         loc = self.get_location_at(c1, True)
 
-        c2 = other.nt_pos_to_contour(other_nt)
+        if other_nt == 0:
+            c2 = 0
+        elif other_nt == other.num_nts-1:
+            c2 = 1
+        else:
+            c2 = other.nt_pos_to_contour(other_nt)
+
         if isinstance(other,SingleStrandedSegment):
             ## Ensure connections occur at opposing ends
-            assert(np.isclose(c2,0) or np.isclose(c2,1))
+            assert(np.isclose(other_nt,0) or np.isclose(other_nt,self.num_nts-1))
             other_loc = other.get_location_at( c2, True )
-            assert( loc.type_ in ("end3","end5"))
-            assert( other_loc.type_ in ("end3","end5"))
-            if loc.type_ == "end3":
+            if ("22-2" in (self.name, other.name)):
+                pdb.set_trace()
+            if nt_on_5prime:
                 self.connect_end3( other_loc )
             else:
-                assert( other_loc.type_ == "end3" )
                 other.connect_end3( self )
 
         else:
-            assert( loc.type_ in ("end3","end5"))
             assert(c2 >= 0 and c2 <= 1)
             other_loc = other.get_location_at( c2, strands_fwd[1] )
-            if loc.type_ == "end3":
+            if nt_on_5prime:
                 self._connect(other, Connection( loc, other_loc, type_="sscrossover" ), in_3prime_direction=True )
             else:
                 other._connect(self, Connection( other_loc, loc, type_="sscrossover" ), in_3prime_direction=True )
@@ -972,7 +978,7 @@ class StrandInSegment(Group):
 
         nts = np.abs(end-start)+1
         self.num_nts = int(round(nts))
-        assert( np.abs(self.num_nts-nts) < 1e-5 )
+        assert( np.isclose(self.num_nts,nts) )
 
         # print(" Creating {}-nt StrandInSegment in {} from {} to {} {}".format(self.num_nts, segment.name, start, end, is_fwd))
     
@@ -1008,6 +1014,7 @@ class Strand(Group):
 
     ## TODO disambiguate names of functions
     def add_dna(self, segment, start, end, is_fwd):
+        # TODOTODO use nt pos ? 
         """ start/end should be provided expressed as contour_length, is_fwd tuples """
         if not (segment.contour_to_nt_pos(np.abs(start-end)) > 0.9):
             import pdb
@@ -1375,23 +1382,44 @@ class SegmentModel(ArbdModel):
             s1,s2 = [l.container for l in (A,B)]
 
             ## TODO be more elegant!
-            if A.on_fwd_strand == False: continue # TODO verify this avoids double-counting
+            # if isinstance(s1, DoubleStrandedSegment) and isinstance(s2, DoubleStrandedSegment) and A.on_fwd_strand == False: continue
+            if isinstance(s1, DoubleStrandedSegment) and isinstance(s2, DoubleStrandedSegment) and A.on_fwd_strand == False: continue
+            ## if A.on_fwd_strand == False: continue # TODO verify this avoids double-counting
             assert( A.particle is None )
             assert( B.particle is None )
 
             ## TODO: offload the work here to s1
+            # TODOTODO
             a1,a2 = [l.address   for l in (A,B)]            
             # a1,a2 = [a - s.nt_pos_to_contour(0.5) if a == 0 else a + s.nt_pos_to_contour(0.5) for a,s in zip((a1,a2),(s1,s2))]
             for a in (a1,a2):
-                assert( a in (0,1,0.0,1.0) )
-            a1,a2 = [a - s.nt_pos_to_contour(0.5) if a == 0 else a + s.nt_pos_to_contour(0.5) for a,s in zip((a1,a2),(s1,s2))]
+                assert( np.isclose(a,0) or np.isclose(a,1) )
+            # a1,a2 = [a - s.nt_pos_to_contour(0) if a == 0 else a + s.nt_pos_to_contour(0) for a,s in zip((a1,a2),(s1,s2))]
             
             ## TODO improve this for combinations of ssDNA and dsDNA (maybe a1/a2 should be calculated differently)
-            if isinstance(s2,DoubleStrandedSegment):
-                b = s2._generate_one_bead(a2,0)
-            else:
-                b = s1._generate_one_bead(a1,0)
+
+            if isinstance(s1,DoubleStrandedSegment):
+                b = s1.get_nearest_bead(a1)
+                if b is not None:
+                    if np.abs(b.get_nt_position(s1) - s1.contour_to_nt_pos(a1)) > 0.5:
+                        b = None
+            elif isinstance(s2,DoubleStrandedSegment):
+                b = s2.get_nearest_bead(a2)
+                if b is not None:
+                    if np.abs(b.get_nt_position(s2) - s2.contour_to_nt_pos(a2)) > 0.5:
+                        b = None
+
+            if b is not None and b.parent not in (s1,s2):
+                b = None
+                
+            if b is None:
+                ## need to generate a bead
+                if isinstance(s2,DoubleStrandedSegment):
+                    b = s2._generate_one_bead(a2,0)
+                else:
+                    b = s1._generate_one_bead(a1,0)
             A.particle = B.particle = b
+            b.locations.extend([A,B])
 
         """ Generate beads at other junctions """
         for c,A,B in self.get_connections(exclude="intrahelical"):
@@ -1406,21 +1434,23 @@ class SegmentModel(ArbdModel):
 
             if A.particle is None:
                 b = s1.get_nearest_bead(a1)
-                if b is not None and s1.contour_to_nt_pos(np.abs(b.contour_position-a1)) < 1.9:
+                if b is not None and s1.contour_to_nt_pos(np.abs(b.contour_position-a1)) < 1:
                     ## combine beads
                     b.contour_position = 0.5*(b.contour_position + a1) # avg position
-                    A.particle = b
                 else:
-                    A.particle = s1._generate_one_bead(a1,0)
+                    b = s1._generate_one_bead(a1,0)
+                A.particle = b
+                b.locations.append(A)
 
             if B.particle is None:
                 b = s2.get_nearest_bead(a2)
-                if b is not None and s2.contour_to_nt_pos(np.abs(b.contour_position-a2)) < 1.9:
+                if b is not None and s2.contour_to_nt_pos(np.abs(b.contour_position-a2)) < 19:
                     ## combine beads
                     b.contour_position = 0.5*(b.contour_position + a2) # avg position
-                    B.particle = b
                 else:
-                    B.particle = s2._generate_one_bead(a2,0)
+                    b = s2._generate_one_bead(a2,0)
+                B.particle = b
+                b.locations.append(B)
 
         """ Some tests """
         for c,A,B in self.get_connections("intrahelical"):
@@ -1445,11 +1475,7 @@ class SegmentModel(ArbdModel):
                 continue
             else:
                 for b in (b1,b2): assert( b is not None )
-                if b2 not in b1.intrahelical_neighbors:
-                    b1.intrahelical_neighbors.append(b2)
-                    b2.intrahelical_neighbors.append(b1)
-                assert(len(b1.intrahelical_neighbors) <= 2)
-                assert(len(b2.intrahelical_neighbors) <= 2)
+                b1.make_intrahelical_neighbor(b2)
 
         """ Reassign bead types """
         if self.DEBUG: print("Assigning bead types")
@@ -1760,6 +1786,7 @@ class SegmentModel(ArbdModel):
             for i in range(len(conn_locs)):
                 c,A,B = conn_locs[i]
                 ## TODO: handle change of direction
+                # TODOTODO
                 address = 1*(direction==-1)
                 if A.address == address and segment_is_new_helix(B.container):
                     new_s = B.container
@@ -1809,39 +1836,40 @@ class SegmentModel(ArbdModel):
         for seg in self.segments:
             ## TODO move into Segment calls
             import pdb
-            if seg.start5.connection is None:
-                add_end = True
-                for l in seg.get_locations("5prime"):
-                    if l.address == 0 and l.on_fwd_strand:
-                        add_end = False
-                        break
-                if add_end:
-                    seg.add_5prime(0) 
-            if 'end5' in seg.__dict__ and seg.end5.connection is None:
-                add_end = True
-                for l in seg.get_locations("5prime"):
-                    if l.address == 1 and (l.on_fwd_strand is False):
-                        add_end = False
-                        break
-                if add_end:
-                    seg.add_5prime(seg.num_nts-1,on_fwd_strand=False)
-
-            if 'start3' in seg.__dict__ and seg.start3.connection is None:
-                add_end = True
-                for l in seg.get_locations("3prime"):
-                    if l.address == 0 and (l.on_fwd_strand is False):
-                        add_end = False
-                        break
-                if add_end:
-                    seg.add_3prime(0,on_fwd_strand=False)
-            if seg.end3.connection is None:
-                add_end = True
-                for l in seg.get_locations("3prime"):
-                    if l.address == 1 and l.on_fwd_strand:
-                        add_end = False
-                        break
-                if add_end:
-                    seg.add_3prime(seg.num_nts-1)
+            if False: # TODO: Make this happen conditionally
+                if seg.start5.connection is None:
+                    add_end = True
+                    for l in seg.get_locations("5prime"):
+                        if l.address == 0 and l.on_fwd_strand:
+                            add_end = False
+                            break
+                    if add_end:
+                        seg.add_5prime(0) 
+                if 'end5' in seg.__dict__ and seg.end5.connection is None:
+                    add_end = True
+                    for l in seg.get_locations("5prime"):
+                        if l.address == 1 and (l.on_fwd_strand is False):
+                            add_end = False
+                            break
+                    if add_end:
+                        seg.add_5prime(seg.num_nts-1,on_fwd_strand=False)
+
+                if 'start3' in seg.__dict__ and seg.start3.connection is None:
+                    add_end = True
+                    for l in seg.get_locations("3prime"):
+                        if l.address == 0 and (l.on_fwd_strand is False):
+                            add_end = False
+                            break
+                    if add_end:
+                        seg.add_3prime(0,on_fwd_strand=False)
+                if seg.end3.connection is None:
+                    add_end = True
+                    for l in seg.get_locations("3prime"):
+                        if l.address == 1 and l.on_fwd_strand:
+                            add_end = False
+                            break
+                    if add_end:
+                        seg.add_3prime(seg.num_nts-1)
 
             # print( [(l,l.get_connected_location()) for l in seg.locations] )
             # addresses = np.array([l.address for l in seg.get_locations("5prime")])
@@ -1857,7 +1885,12 @@ class SegmentModel(ArbdModel):
                 import pdb
                 pdb.set_trace()
             s,seg = [strand, segment]
-
+            
+            #if seg.name == "22-1" and pos > 140:
+            if seg.name == "22-2":
+                import pdb
+                pdb.set_trace()
+            
             end_pos, next_seg, next_pos, next_dir, move_at_least = seg.get_strand_segment(pos, is_fwd, move_at_least)
             s.add_dna(seg, pos, end_pos, is_fwd)
 
@@ -1871,6 +1904,7 @@ class SegmentModel(ArbdModel):
             # for pos, is_fwd in locs:
             for l in locs:
                 print("Tracing",l)
+                # TODOTODO
                 pos = seg.contour_to_nt_pos(l.address, round_nt=True)
                 is_fwd = l.on_fwd_strand
                 s = Strand()