diff --git a/mrdna/readers/__init__.py b/mrdna/readers/__init__.py
index 5e9a828d00e815cd50694180856df4f5aa083f73..31a6cedd5f7f92641d81ac6691280103a97c4e51 100644
--- a/mrdna/readers/__init__.py
+++ b/mrdna/readers/__init__.py
@@ -8,15 +8,11 @@ def read_cadnano(json_file, **model_parameters):
     return model_from_cadnano_json(data, **model_parameters)
 
 def read_vhelix(maya_file, **model_parameters):
-    from .polygon_mesh import parse_maya_file, convert_maya_to_segments
+    from .polygon_mesh import parse_maya_file, convert_maya_bases_to_segment_model
     from ..model.dna_sequence import m13 as m13seq
-    from ..segmentmodel import SegmentModel
 
-    data = parse_maya_file(maya_file)
-    segments, dsSegmentDict = convert_maya_to_segments( data )
-    if 'dimensions' not in model_parameters:
-        model_parameters['dimensions']=(5000,5000,5000)
-    model = SegmentModel( segments,**model_parameters )
+    maya_bases = parse_maya_file(maya_file)
+    model = convert_maya_bases_to_segment_model( maya_bases, **model_parameters )
     model.set_sequence(m13seq*10)
     return model
 
diff --git a/mrdna/readers/polygon_mesh.py b/mrdna/readers/polygon_mesh.py
index 700ad09fd34295430323d766254195bd4e6b4519..2eb0015f740581ac76548f6998334a68c5a20dc4 100644
--- a/mrdna/readers/polygon_mesh.py
+++ b/mrdna/readers/polygon_mesh.py
@@ -1,16 +1,42 @@
 import numpy as np
 import sys
 import re
-from ..coords import rotationAboutAxis
+from ..coords import rotationAboutAxis, minimizeRmsd
 
+from ..version import maintainer
 from ..segmentmodel import SegmentModel, SingleStrandedSegment, DoubleStrandedSegment
 
+DEBUG=False
+
+maya_obj_registry = dict()
+maya_obj_registry_short = dict()
 class MayaObj():
+    """ Class representing a node in a Maya ascii file """
+
     def __init__(self, maya_lines):
+        self.parent = None
+        self.children = dict()
+        self.position = np.array((0,0,0))
+        self.orientation = np.eye(3)
+
         self.name, self.parent_name = MayaObj._parse_first_maya_line( maya_lines[0] )
+        self._parse_maya_lines(maya_lines)
+
+        if self.parent_name is not None:
+            if self.parent_name in maya_obj_registry:
+                p = maya_obj_registry[self.parent_name]
+                p.add(self)
+            elif self.parent_name in maya_obj_registry_short:
+                p = maya_obj_registry_short[self.parent_name]
+                p.add(self)
+            else:
+                raise Exception("Parent for object not yet defined")
+
+        maya_obj_registry[self.get_full_name()] = self
+        maya_obj_registry_short[self.name] = self
 
     def _parse_first_maya_line(l):
-        ## createNode vHelix -n "helix_1";
+        """ Extracts name and parent from maya file """
         name = parent = None
         vals = l.split()
         for i in range(len(vals)):
@@ -19,10 +45,59 @@ class MayaObj():
             if vals[i] == "-p":
                 parent = MayaObj._strip_maya_string( vals[i+1] )
         return name,parent
-    
+
+    def _parse_maya_lines(self, maya_lines):
+        """ Sets position and orientation from attributes in vHelix maya files """
+        for l in maya_lines:
+            vals = l.split()
+            if vals[0] == "setAttr":
+                if vals[1] == '".t"':
+                    self.position = 10*np.array([float(a) for a in vals[4:7]])
+                elif vals[1] in ('".r"','".rsrr"'):
+                    ax,ay,az = [float(a) for a in vals[4:7]]
+                    Rx = rotationAboutAxis( [1,0,0], ax, normalizeAxis=False )
+                    Ry = rotationAboutAxis( [0,1,0], ay, normalizeAxis=False )
+                    Rz = rotationAboutAxis( [0,0,1], az, normalizeAxis=False )
+                    self.orientation = Rz.dot(Ry.dot(Rx))
+
     def _strip_maya_string(string):
-        return re.match('"([a-zA-Z_[0-9]+)";?$', string).group(1)
+        return re.match('"\|?([a-zA-Z_0-9|]+)";?$', string).group(1)
 
+    def add(self, obj):
+        self.children[obj.name] = obj
+        obj.parent = self
+
+    def get_position(self):
+        if self.parent is not None:
+            return self.parent._transform_child_position(self.position)
+        else:
+            return self.position
+
+    def get_orientation(self):
+        if self.parent is not None:
+            return self.parent._transform_child_orientation(self.orientation)
+        else:
+            return self.orientation
+
+    def _transform_child_position(self, position):
+        if self.parent is not None:
+            p = self.parent._transform_child_position( position )
+        else:
+            p = position
+        return self.orientation.dot(p)+self.position
+
+    def _transform_child_orientation(self, orientation):
+        if self.parent is not None:
+            o = self.parent._transform_child_orientation( orientation )
+        else:
+            o = orientation
+        return self.orientation.dot(o)
+
+    def get_full_name(self):
+        ret = self.name
+        if self.parent is not None:
+            ret = '{}|{}'.format(self.parent.get_full_name(), ret)
+        return ret
 
 class MayaConnection():
     def __init__(self, helix1, base1, suff1, helix2, base2, suff2):
@@ -33,8 +108,6 @@ class MayaConnection():
         self.base2  = base2
         self.suff2  = suff2
 
-# MayaConnections = dict()        
-
 def ParseMayaConnection(line, base_dict):
     words = line.split()
     if len(words) != 3:
@@ -63,79 +136,14 @@ def ParseMayaConnection(line, base_dict):
             return MayaConnection(helix1,base1,suff1,helix2,base2,suff2)
     return None
 
-
-class MayaHelix(MayaObj):
-    def __init__(self, maya_lines):
-        MayaObj.__init__(self, maya_lines)
-        # self.bases = []
-        self.bases = dict()
-        self.position = None
-        self.orientation = None
-
-        self._parse_maya_lines(maya_lines)
-        
-    def _parse_maya_lines(self, maya_lines):
-        """
-createNode vHelix -n "helix_30";
-setAttr ".t" -type "double3" 9.24809 21.4638 -12.465 ;
-setAttr ".r" -type "double3" 28.99971393816978 2.2177410772603801 87.637950315471372 ;
-setAttr ".dh" yes;
-        """
-        for l in maya_lines:
-            vals = l.split()
-            if vals[0] == "setAttr":
-                if vals[1] == '".t"':
-                    self.position = 10*np.array([float(a) for a in vals[4:7]])
-                elif vals[1] == '".r"':
-                    # self.eulerAngles = np.array([float(a) for a in vals[4:7]]) # defined here
-                    ax,ay,az = [float(a) for a in vals[4:7]] 
-                    Rx = rotationAboutAxis( [1,0,0], ax, normalizeAxis=False )
-                    Ry = rotationAboutAxis( [0,1,0], ay, normalizeAxis=False )
-                    Rz = rotationAboutAxis( [0,0,1], az, normalizeAxis=False )
-                    self.orientation = Rz.dot(Ry.dot(Rx)) # TODO: check
-                    # rotXYZ = np.array()
-
-    def add_base(self, base):
-        # self.bases.append(base)
-        self.bases[base.name] = base
-        base.parent = self
-
-    def find_double_helix_regions(self):
-        double_helices = []
-        for bn,b1 in self.bases.items():
-            b2 = b1.basepair
-            if b2 is None: continue
-            d =  np.linalg.norm( b1.get_position()-b2.get_position() )
-            print( d )
-            
-
-    def write_xyz(self,file_handle = sys.stdout):
-        for bn,b in self.bases.items():
-            position = b.get_position()
-            # position = b.position.dot( self.orientation ) + self.position # TODO: double check
-            file_handle.write("%s %f %f %f\n" % (b.name[0], position[0], position[1], position[2]))
-
-
 class MayaBase(MayaObj):
     def __init__(self, maya_lines):
         MayaObj.__init__(self, maya_lines)
         self._parse_maya_lines(maya_lines)        
         self.basepair = None
-        self.parent = None
         self.end3 = None
         self.end5 = None
 
-    def _parse_maya_lines(self, maya_lines):
-        """
-createNode HelixBase -n "backw_1" -p "helix_30";
-setAttr ".t" -type "double3" 0.36767788771440857 -0.78848777472188547 -6.014 ;
-        """
-        for l in maya_lines:
-            vals = l.split()
-            if vals[0] == "setAttr":
-                if vals[1] == '".t"':
-                    self.position = 10*np.array([float(a) for a in vals[4:7]])
-
     def add_basepair(self, base):
         assert( self.parent == base.parent )
         assert( self.basepair is None or self.basepair == base )
@@ -151,27 +159,22 @@ setAttr ".t" -type "double3" 0.36767788771440857 -0.78848777472188547 -6.014 ;
 
     def add_end5(self, base):
         base.add_end3(self)
-    
-    def get_position(self):
-        h = self.parent
-        return h.orientation.dot(self.position) + h.position # TODO: double check
-        
-
-# class MayaAimConstraint(MayaObj):
-#     ...
-
-
-# set ch [open /home/cmaffeo2/Downloads/ball.ma]
 
+    def __repr__(self):
+        return "Base {} {:.2f}".format(self.name, self.position[2])
 
 def parse_maya_file(maya_file):
+    """ Function to parse vHelix maya ascii file, extracting useful information about base """
+
+    objects = []
+    objects_by_full_name = dict()
 
     helices = dict()
     bases = []
-    aimConstraints = []
+    aim_constraints = []
     connections = []
 
-    ## Parse through .ma file
+    """ Parse .ma file """
     with open(maya_file) as fh:
         linesBuffer = []
         for line in fh:
@@ -182,12 +185,15 @@ def parse_maya_file(maya_file):
                 if len(linesBuffer) > 0:
                     objType = linesBuffer[0].split()[1]
                     if objType == "vHelix":
-                        h = MayaHelix( linesBuffer )
-                        helices[h.name] = h
+                        h = MayaObj( linesBuffer )
+                        helices[h.get_full_name()] = h
                     elif objType == "HelixBase":
                         bases.append( MayaBase( linesBuffer ) )
-                    # elif objType == "aimConstraint":
-                    #     aimConstraints.append( MayaAimConstraint( linesBuffer ) )
+                    elif objType == "transform":
+                        o = MayaObj( linesBuffer )
+                        objects.append(o)
+                    elif objType == "aimConstraint":
+                        aim_constraints.append( MayaObj( linesBuffer ) )
                 ## Clear lines buffer
                 linesBuffer = []
             elif words[0] == "connectAttr":
@@ -195,13 +201,8 @@ def parse_maya_file(maya_file):
                 
             ## Extend lines buffer
             linesBuffer.append(line)
-            
-    ## Crawl through bases, attach to parent helix
-    for b in bases:
-        helix_name = b.parent_name
-        helices[helix_name].add_base( b ) # function updates base position?
 
-    ## Parse connections
+    """ Parse connections """
     new_connections = []
     base_dict = { b.name:b for b in bases }
     for line in connections:        
@@ -210,7 +211,7 @@ def parse_maya_file(maya_file):
             new_connections.append(conn)
     connections = new_connections
 
-    ## Assign base connectivity
+    """ Assign base connectivity """
     for c in connections:
         h1 = c.helix1
         bn1 = c.base1
@@ -219,349 +220,166 @@ def parse_maya_file(maya_file):
         bn2 = c.base2
         s2 = c.suff2
         
-        b1 = helices[h1].bases[bn1]
-        b2 = helices[h2].bases[bn2]
+        b1 = helices[h1].children[bn1]
+        b2 = helices[h2].children[bn2]
         if s1 == "lb" and s2 == "lb":
             b1.add_basepair(b2)
-        # elif (bn1[0] == "f" and s1 == "fw" and s2 == "bw") or \
-        #      (bn1[0] == "b" and s1 == "bw" and s2 == "fw"):
-        #     b1.add_end3(b2)
-        # elif (bn1[0] == "f" and s1 == "bw" and s2 == "fw") or \
-        #      (bn1[0] == "b" and s1 == "fw" and s2 == "bw"):
-        #     b1.add_end5(b2)
         elif s1 == "fw" and s2 == "bw":
-            b1.add_end3(b2)
-        elif s1 == "bw" and s2 == "fw":
             b1.add_end5(b2)
+        elif s1 == "bw" and s2 == "fw":
+            b1.add_end3(b2)
         else:
-            raise Exception("Unkown connection %s %s" % (s1,s2))
+            raise Exception("Unrecognized connection %s %s" % (s1,s2))
 
-    # # base_positions = np.zeros( [ len(bases), 3 ] )
-    # base_positions = np.array( [b.position for b in bases] )
-    # for aim in aimConstraints:
-    #     base = base_dict[ aim.parent_name ]
+    """ Find local basis of each base so that
+    model_from_basepair_stack_3prime can determine stacks """
 
-    return helices
+    ref_below_position = np.array((5.11, -0.4, -3.34))
+    ref_stack_position = np.array((-1.5, -4.7, 3.34))
+    ref_bp_position = np.array((0.042, -16.98, 0.0))
+    ref_bp_below_position = np.array((4.2, -12.9, 3.34))
+    ref_bp_stack_position = np.array((-4.42, -14.46, -3.34))
 
-def convert_maya_to_segments(helices):
+    ref_positions = np.array((ref_below_position,
+                              ref_stack_position,
+                              ref_bp_position,
+                              ref_bp_below_position,
+                              ref_bp_stack_position))
 
-    def get_end3_ssDNA(nt):
-        ret = []
-        if nt.end3 is not None:
-            # print( nt.parent.name, nt.end3.parent.name, nt.end3.name )
-            nt2 = nt.end3
-            if nt2.basepair is None:
-                ret.append(nt2)
-                ret.extend(get_end3_ssDNA(nt2))
-        return ret
+    shift = np.array((1.19, -3.52, 0.08))
 
-    def get_end5_ssDNA(nt):
-        ret = []
-        if nt.end5 is not None:
-            nt2 = nt.end5
-            if nt2.basepair is None:
-                ret.append(nt2)
-                ret.extend(get_end5_ssDNA(nt2))
-        return ret
+    if DEBUG:
+        write_pdb_psf(bases, 'before')
 
-    """ Build dsDNA helices """
-    segments = dict()
-    segmentsBot = dict()
-    segmentsTop = dict()
-    for hn,h in helices.items():
-        paired_bases = [b for bn,b in h.bases.items() if b.basepair is not None]
-        upaired = [b for bn,b in h.bases.items() if b.basepair is None]
-        # for b in upaired:
-        #     print(hn, b.name, b.end3, b.end5)
+    def update_geometry(b,beads):
         
-        ## Find helical axis
-        # if len(paired_bases) < 6:
-        #     raise Exception("Helices shorter than 3 bp are not supported")
-        # coords = np.array([b.position for b in paired_bases])
-        # u,s,vh = np.linalg.svd(coords)
-        # axis = vh[0] / np.linalg.norm(vh[0])
-
-        fwd_bases = list(filter(lambda b:b.name[0] == 'f', paired_bases))
-        # rank = [np.dot(b.position,axis) for b in fwd_bases]
-        rank = [b.position[2] for b in fwd_bases]
-        order = np.argsort(rank)
-        ordered_fwd_bases = [fwd_bases[o] for o in order]
-        next_fwd_is_3end = [b1.end3==b2 for b1,b2 in
-                            zip(ordered_fwd_bases[:-1],ordered_fwd_bases[1:])]
-
-        ## Check if axis points the wrong way
-        assert( np.mean(next_fwd_is_3end) > 0.5 )
-        # if np.mean( next_fwd_is_3end ) < 0.5:
-        #     # axis = axis * -1
-        #     rank = rank[order[-1]]-rank
-        #     order = reversed(order)
-        #     ordered_fwd_bases = reversed(ordered_fwd_bases)
-
-        top = ordered_fwd_bases[-1]
-        bot = ordered_fwd_bases[0].basepair
-
-        assert( bot.name[0] == 'b' )
-        assert( top.name[0] == 'f' )
-
-        botPos = 0.5 * (bot.get_position() + bot.basepair.get_position())
-        topPos = 0.5 * (top.get_position() + top.basepair.get_position())
-
-        segname = hn
-        if segname[:6] == "helix_":
-            segname = 'D'+segname[6:]
-        segments[hn] =  DoubleStrandedSegment(segname, num_bp = len(fwd_bases),
-                                              start_position = botPos,
-                                              end_position = topPos)
-
-        ## Add 3prime/5prime ends
-        nt = 0
-        def add_primes(b,nt,fwd):
-            if b.end3 is None:
-                segments[hn].add_3prime(nt,fwd)
-            if b.end5 is None:
-                segments[hn].add_5prime(nt,fwd)
-
-        for b in ordered_fwd_bases:
-            add_primes(b,nt,True)
-            add_primes(b.basepair,nt,False)
-            nt+=1
-
-        segmentsBot[hn] = bot
-        segmentsTop[hn] = top
-        assert( bot.end3 not in paired_bases )
-        assert( top.end3 not in paired_bases )
-
-    """ Sanity check """
-    def end_crosses(end,direction):
-        assert(direction in ("end3","end5"))
-        other = end.__dict__[direction]
-        if other is None or end.parent != other.parent:
-            return 1
-        else:
-            return 0
-
-    hs = segments.keys()
-    botCrossings1 = [end_crosses(segmentsBot[h],"end3") for h in hs]
-    botCrossings2 = [end_crosses(segmentsBot[h].basepair,"end5") for h in hs]
-    topCrossings1 = [end_crosses(segmentsTop[h],"end3") for h in hs]
-    topCrossings2 = [end_crosses(segmentsTop[h].basepair,"end5") for h in hs]
-    # print( [np.mean(a) for a in (botCrossings1,botCrossings2,topCrossings1,topCrossings2)] )
-
-    # for a in (botCrossings1,botCrossings2,topCrossings1,topCrossings2:
-    #     print(np.mean(a))
-    #     assert( np.mean(a) > 0.5 )
-
-    """ Build ssDNA segments """
-    ss_segments = []
-    connectionToSingleStrand = dict()
-    for hn,seg in segments.items():
-        bot = segmentsBot[hn]
-        top = segmentsTop[hn]
-        # if top.end3 is not None:
-        #     print(top.parent.name,top.name, top.end3.parent.name, top.end3.name)
-        # if bot.end3 is not None:
-        #     print(bot.parent.name,bot.name, bot.end3.parent.name, bot.end3.name)
-
-        ## TODO: clean this up
-        def get_end_positions(ssSeg, is_fwd):
-            start_idx, end_idx = (0,-1)
-            if not is_fwd:
-                start_idx, end_idx = (-1,0)
-
-            r0 = ssSeg[0].get_position()
-            if ssSeg[0].end5 is not None:
-                r0 = 0.75*r0 + 0.25*ssSeg[0].end5.get_position()
-            r1 = ssSeg[-1].get_position()
-            if ssSeg[-1].end3 is not None:
-                r1 = 0.75*r1 + 0.25*ssSeg[-1].end3.get_position()
-            return r0,r1
-
-        if top not in connectionToSingleStrand:
-            ssSeg = get_end3_ssDNA(top)
-            num_nt = len(ssSeg)
-            if num_nt > 0:
-                r0,r1 = get_end_positions(ssSeg,is_fwd=True)
-                seg2 = SingleStrandedSegment("S{:03d}".format(len(ss_segments)), 
-                                              start_position = r0,
-                                              end_position = r1,
-                                              num_nt = num_nt)
-                seg.connect_end3(seg2)
-                connectionToSingleStrand[top] = seg2
-                other = ssSeg[-1].end3
-                if other is not None:
-                    hn3 = other.parent.name
-                    seg3 = segments[hn3]
-                    bot3 = segmentsBot[hn3]
-                    top3 = segmentsTop[hn3]
-                    # if bot3 == other or bot3.basepair == other:
-                    #     seg3.connect_start5(seg2)
-                    # elif top3 == other or top3.basepair == other:
-                    #     seg3.connect_end5(seg2)
-                    if bot3.basepair == other:
-                        seg3.connect_start5(seg2)
-                    elif top3.basepair == other:
-                        seg3.connect_end5(seg2)
-                    else:
-                        raise Exception("BUG")
-                    connectionToSingleStrand[other] = seg2
-                ss_segments.append(seg2)
-
-        if top.basepair not in connectionToSingleStrand:
-            ssSeg = get_end5_ssDNA(top.basepair)[::-1]
-            if len(ssSeg) > 0:
-                r0,r1 = get_end_positions(ssSeg,is_fwd=False)
-                seg2 = SingleStrandedSegment("S{:03d}".format(len(ss_segments)),
-                                              start_position = r0,
-                                              end_position = r1,
-                                              num_nt = len(ssSeg))
-                seg.connect_end5(seg2)
-                connectionToSingleStrand[top.basepair] = seg2
-                other = ssSeg[0].end5
-                if other is not None:
-                    hn3 = other.parent.name
-                    seg3 = segments[hn3]
-                    bot3 = segmentsBot[hn3]
-                    top3 = segmentsTop[hn3]
-                    # if bot3 == other or bot3.basepair == other:
-                    #     seg3.connect_start5(seg2)
-                    # elif top3 == other or top3.basepair == other:
-                    #     seg3.connect_end5(seg2)
-                    if bot3 == other:
-                        seg3.connect_start3(seg2)
-                    elif top3 == other:
-                        seg3.connect_end3(seg2)
-                    else:
-                        raise Exception("BUG")
-                    connectionToSingleStrand[other] = seg2
-                ss_segments.append(seg2)
-
-        if bot not in connectionToSingleStrand:
-            ssSeg = get_end3_ssDNA(bot)
-            if len(ssSeg) > 0:
-                r0,r1 = get_end_positions(ssSeg,is_fwd=True)
-                seg2 = SingleStrandedSegment("S{:03d}".format(len(ss_segments)),
-                                              start_position = r0,
-                                              end_position = r1,
-                                              num_nt = len(ssSeg))
-                seg.connect_start3(seg2)
-                connectionToSingleStrand[bot] = seg2
-                other = ssSeg[-1].end3
-                if other is not None:
-                    hn3 = other.parent.name
-                    seg3 = segments[hn3]
-                    bot3 = segmentsBot[hn3]
-                    top3 = segmentsTop[hn3]
-                    if bot3.basepair == other:
-                        seg3.connect_start5(seg2)
-                    elif top3.basepair == other:
-                        seg3.connect_end5(seg2)
-                    else:
-                        raise Exception("BUG")
-                    connectionToSingleStrand[other] = seg2
-                ss_segments.append(seg2)
-
-        if bot.basepair not in connectionToSingleStrand:
-            ssSeg = get_end5_ssDNA(bot.basepair)[::-1]
-            if len(ssSeg) > 0:
-                r0,r1 = get_end_positions(ssSeg,is_fwd=False)
-                seg2 = SingleStrandedSegment("S{:03d}".format(len(ss_segments)),
-                                              start_position = r0,
-                                              end_position = r1,
-                                              num_nt = len(ssSeg))
-                seg.connect_start5(seg2)
-                connectionToSingleStrand[bot.basepair] = seg2
-                other = ssSeg[0].end5
-                if other is not None:
-                    hn3 = other.parent.name
-                    seg3 = segments[hn3]
-                    bot3 = segmentsBot[hn3]
-                    top3 = segmentsTop[hn3]
-                    if bot3 == other:
-                        seg3.connect_start3(seg2)
-                    elif top3 == other:
-                        seg3.connect_end3(seg2)
-                    else:
-                        raise Exception("BUG")
-                    connectionToSingleStrand[other] = seg2
-                ss_segments.append(seg2)
-                
-    ## Connect remaining dsDNA ends
-    no_connection_count = 0
-    for hn,seg in segments.items():
-        bot = segmentsBot[hn]
-        top = segmentsTop[hn]
-
-        top_bp, bot_bp = [b.basepair for b in (top,bot)]
+        """ Update the position and orientation of bead b using the 5
+        neighboring bases in "beads" as a reference """
         
-        if top not in connectionToSingleStrand and top.end3 is not None:
-            b2 = top.end3
-            h2 = b2.parent.name
-            s2 = segments[h2]
-            if b2 == segmentsBot[h2] or b2.basepair == segmentsBot[h2]:
-                seg.connect_end3(s2.start5,type_="terminal_crossover")
-            elif b2 == segmentsTop[h2] or b2.basepair == segmentsTop[h2]:
-                seg.connect_end3(s2.end5,type_="terminal_crossover")
-            else:
-                raise Exception("BUG")
+        def _get_beads_index(i):
+            j=0
+            while j <= i:
+                if beads[j] is None:
+                    i+=1
+                j+=1
+            return j-1
+
+        """ Get position and filter out beads == None """
+        r0 = b.get_position()
+        tmp_ref_positions = ref_positions[[b2 is not None for b2 in beads]]
+        positions = np.array([b2.get_position()-r0 for b2 in beads if b2 is not None])
+        dist = np.linalg.norm(positions,axis=-1)
+
+        """ Discard neighboring bases that are very far from current base """
+        if np.any(dist > 20) and len(positions) > 3:
+            i = _get_beads_index(np.argmax(dist))
+            beads[i] = None
+            return update_geometry(b, beads)
+
+        """ Find transform that fits neighborhood around base to a reference """
+        R,comB,comA = minimizeRmsd(positions, tmp_ref_positions)
+
+        dr = np.array([R.T.dot(p-comB)-p0+comA for p,p0 in zip(positions, tmp_ref_positions)])
+        dr2 = (dr**2).sum(axis=-1)
+        msd = dr2.sum()/len(positions)
+
+        """ Discard neighboring bases that are very far from their expected position """
+        if msd > 17 and len(positions) > 3:
+            # print("WARNING:",b,msd)
+            # print(dr2)
+            i = _get_beads_index(np.argmax(dr2))
+            beads[i] = None
+            return update_geometry(b,beads)
         
-        if top_bp not in connectionToSingleStrand and top_bp.end5 is not None:
-            b2 = top_bp.end5
-            h2 = b2.parent.name
-            s2 = segments[h2]
-            if b2 == segmentsBot[h2] or b2.basepair == segmentsBot[h2]:
-                seg.connect_end5(s2.start3,type_="terminal_crossover")
-            elif b2 == segmentsTop[h2] or b2.basepair == segmentsTop[h2]:
-                seg.connect_end5(s2.end3, type_="terminal_crossover")
-            else:
-                raise Exception("BUG")
-
-        if bot not in connectionToSingleStrand and bot.end3 is not None:
-            b2 = bot.end3
-            h2 = b2.parent.name
-            s2 = segments[h2]
-            if b2 == segmentsBot[h2] or b2.basepair == segmentsBot[h2]:
-                seg.connect_start3(s2.start5,type_="terminal_crossover")
-            elif b2 == segmentsTop[h2] or b2.basepair == segmentsTop[h2]:
-                seg.connect_start3(s2.end5,type_="terminal_crossover")
-            else:
-                raise Exception("BUG")
-        
-        if bot_bp not in connectionToSingleStrand and bot_bp.end5 is not None:
-            b2 = bot_bp.end5
-            h2 = b2.parent.name
-            s2 = segments[h2]
-            if b2 == segmentsBot[h2] or b2.basepair == segmentsBot[h2]:
-                seg.connect_start5(s2.start3,type_="terminal_crossover")
-            elif b2 == segmentsTop[h2] or b2.basepair == segmentsTop[h2]:
-                seg.connect_start5(s2.end3, type_="terminal_crossover")
-            else:
-                raise Exception("BUG")
+        """ Assign position and orientation to base """
+        if b.parent is None:
+            o_parent = np.eye(3)
+        else:
+            o_parent = b.parent.get_orientation()
+
+        b.orientation = o_parent.T.dot( R )
+        b.position = b.position + b.orientation.dot( shift )
 
-    return [s for n,s in segments.items()] + ss_segments, segments
+    """ Loop over bases updating their positions and orientations """
+    for b in bases:
+        if b.basepair is not None:
+            beads = [b.end5, b.end3, b.basepair, b.basepair.end5, b.basepair.end3]
+            update_geometry(b,beads)
+
+    if DEBUG:
+        write_pdb_psf(bases, 'after')
+
+    return bases
+
+
+def write_pdb_psf(bases, prefix):
+
+    """ Function for debugging .ma parser """
+
+    from ..model.arbdmodel import ArbdModel,ParticleType, PointParticle, Group
+    from ..model.nonbonded import HarmonicBond
+
+    types = {c:ParticleType(c.upper(), mass=1,radius=1) for c in "bfuxyzs"}
+    bond=HarmonicBond(k=0,r0=1)
+
+    base_to_particle = dict()
+    seg = []
+    count=0
+    for b in bases:
+        r = b.get_position()
+        o = b.get_orientation()
+
+        type_character = 'f' if re.match('.*forw.*',b.name) else 'b' if re.match('.*bac.*',b.name) else 'u'
+        name = type_character + b.name.split("_")[-1]
+        p = PointParticle( name=name, type_=types[type_character], position = r )
+        particles = [p]
+        base_to_particle[b] = p
+        for axis,c in zip(np.eye(3),"xyz"):
+            particles.append( PointParticle( name=c, type_=types[c],
+                                        position = r + o.T.dot(axis) ))
+
+        ref_stack_position = np.array((-2.41851735, -0.259761333, 3.39999978))
+        particles.append( PointParticle( name=c, type_=types[c],
+                                         position = r + o.dot(ref_stack_position) ))
+
+        res = Group(name='r'+str(count), children=particles, resid=count)
+        count+=1
+        for p2 in particles[1:]:
+            res.add_bond(p,p2, bond=bond)
+        seg.append(res)
+
+    model = ArbdModel(seg)
+    for b in bases:
+        if b.end3 is not None:
+            model.add_bond( base_to_particle[b], base_to_particle[b.end3], bond=bond )
+        if b.basepair is not None:
+            model.add_bond( base_to_particle[b], base_to_particle[b.basepair], bond=bond )
 
+    model._countParticleTypes()
+    model._updateParticleOrder()
+    model.writePsf(prefix+'.psf')
+    model.writePdb(prefix+'.pdb')
 
-def write_xyz(filename, helices):
-    nbeads = 0
-    for hn,h in helices.items():
-        nbeads += len(h.bases)
 
-    with open("test.xyz","w") as fh:
-        fh.write("%d\n\n" % nbeads)
-        for hn,h in helices.items():
-            h.write_xyz(fh)
-            #      h.find_double_stranded_regions
+def convert_maya_bases_to_segment_model(maya_bases, **model_parameters):
 
+    """ Converts bases parse from .ma file into mrdna an model """
 
-if __name__ == "__main__":
-    maya_helices = parse_maya_file("/home/cmaffeo2/Downloads/bunny.ma")
+    from .segmentmodel_from_lists import model_from_basepair_stack_3prime
 
-    segments,dsSegmentDict = convert_maya_to_segments(maya_helices)
-    
-    model = SegmentModel( segments,
-                          local_twist = True,
-                          max_basepairs_per_bead = 1,
-                          max_nucleotides_per_bead = 1,
-                          dimensions=(5000,5000,5000), DEBUG=True )
+    base_to_index = {b:i for i,b in zip(range(len(maya_bases)),maya_bases)}
+    coord = np.zeros((len(maya_bases),3))
+    orientation = np.zeros((len(maya_bases),3,3))
+    bp,end3 = [-np.ones((len(maya_bases)),dtype=np.int) for i in range(2)]
 
-    model.simulate( outputPrefix = 'bunny2', outputPeriod=1e4, numSteps=1e8, gpu=1 )
+    for i,b in zip(range(len(maya_bases)),maya_bases):
+        coord[i] = b.get_position()
+        orientation[i] = b.get_orientation().T
+        if b.basepair is not None:
+            bp[i] = base_to_index[ b.basepair ]
+        if b.end3 is not None:
+            end3[i] = base_to_index[ b.end3 ]
 
+    return model_from_basepair_stack_3prime(coord, bp, None, end3,
+                                            orientation=orientation,
+                                            **model_parameters)