From 385f358ce2b4e95d01f5c30b72eca5628a6c6643 Mon Sep 17 00:00:00 2001
From: Chris Maffeo <cmaffeo2@illinois.edu>
Date: Fri, 21 Feb 2020 16:57:00 -0600
Subject: [PATCH] Option and commands to convert crossovers into intrahelical

---
 bin/mrdna             | 12 +++++++++
 mrdna/segmentmodel.py | 60 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+)

diff --git a/bin/mrdna b/bin/mrdna
index 0c7d04e..e43df23 100755
--- a/bin/mrdna
+++ b/bin/mrdna
@@ -56,6 +56,9 @@ parser.add_argument('--oxdna-output-period', type=float, default=1e4,
 parser.add_argument('--coarse-bond-cutoff', type=float, default = 0,
                     help='Ignore bonds beyond this cutoff during first step of simulation; a value of 0 implies bonds are not ignored')
 
+parser.add_argument('--crossover-to-intrahelical-cutoff', type=float, default=-1,
+                    help='Set the distance (in Angstroms) beyond which crossovers between helix ends are converted to intrahelical connections; a negative value means no crossovers will be converted')
+
 parser.add_argument('--backbone-scale', type=float, default=1.0,
                     help='Factor to scale DNA backbone in atomic model; try 0.25 to avoid clashes for atomistic simulations')
 
@@ -119,6 +122,15 @@ def main():
     else:
         model.dimensions = args.dimensions
 
+    if args.crossover_to_intrahelical_cutoff >= 0:
+        model.convert_crossovers_at_ends_beyond_cutoff_to_intrahelical(
+            args.crossover_to_intrahelical_cutoff)
+
+    if args.sequence_file is not None:
+        from mrdna.model.dna_sequence import read_sequence_file
+        seq = read_sequence_file(args.sequence_file)
+        model.set_sequence( seq )
+
     if args.output_prefix is not None:
         prefix = args.output_prefix
     
diff --git a/mrdna/segmentmodel.py b/mrdna/segmentmodel.py
index 8867774..6a8c6a6 100644
--- a/mrdna/segmentmodel.py
+++ b/mrdna/segmentmodel.py
@@ -8,6 +8,8 @@ from .arbdmodel.nonbonded import *
 from copy import copy, deepcopy
 from .model.nbPot import nbDnaScheme
 
+import re
+
 from scipy.special import erf
 import scipy.optimize as opt
 from scipy import interpolate
@@ -2027,6 +2029,64 @@ class SegmentModel(ArbdModel):
                 tck, u = interpolate.splprep( quats.T, u=contours, s=0, k=1 )
                 s.quaternion_spline_params = (tck,u)
 
+    def get_segments_matching(self, pattern):
+        """ Returns a list of all segments that match the regular expression 'pattern' """
+
+        re_pattern = re.compile(pattern)
+        return [s for s in self.segments if re_pattern.search(s.name) is not None]
+
+    def get_crossovers_at_ends(self):
+        """
+        Return a list of all "regular" crossovers where both sides of
+        the crossover exist at the start or end of a segment
+        """
+
+        ret_list = []
+        for s in self.segments:
+            for c in filter(lambda c: c.type_ == "crossover", s.connections):
+                seg1 = c.A.container
+                seg2 = c.B.container
+                nt1 = c.A.get_nt_pos()
+                nt2 = c.B.get_nt_pos()
+                if (nt1 < 1 and nt2 > seg2.num_nt-2) or (nt2 < 1 and nt1 > seg1.num_nt-2):
+                    if c not in ret_list:
+                        ret_list.append(c)
+        return ret_list
+
+    def convert_crossover_to_intrahelical(self, crossover):
+        c = crossover
+        seg1 = c.A.container
+        seg2 = c.B.container
+        nt1 = c.A.get_nt_pos()
+        nt2 = c.B.get_nt_pos()
+
+        assert( c.A.on_fwd_strand == c.B.on_fwd_strand )
+        on_fwd_strand = c.A.on_fwd_strand
+
+        c.delete()
+
+        if nt1 < 1 and nt2 > seg2.num_nt-2:
+            if on_fwd_strand:
+                seg2.connect_end3(seg1.start5)
+            else:
+                seg1.connect_start3(seg2.end5)
+
+        elif nt2 < 1 and nt1 > seg1.num_nt-2:
+            if on_fwd_strand:
+                seg1.connect_end3(seg2.start5)
+            else:
+                seg2.connect_start3(seg1.end5)
+
+    def convert_crossovers_at_ends_beyond_cutoff_to_intrahelical(self,cutoff):
+        if cutoff < 0:
+            raise ValueError("Cutoff should be positive!")
+
+        for c in self.get_crossovers_at_ends():
+            r1,r2 = [l.container.contour_to_position(l.address)
+                     for l in (c.A,c.B)]
+            if np.linalg.norm(r2-r1) > cutoff:
+                self.convert_crossover_to_intrahelical(c)
+
     def _generate_bead_model(self,
                              max_basepairs_per_bead = 7,
                              max_nucleotides_per_bead = 4,
-- 
GitLab