Difference between revisions of "User:Remig"

From Jmol
Jump to navigation Jump to search
m
(Revision of Polymeraze)
Line 1: Line 1:
My interest is in protein folding for which I have written software tools in Java over the years. I had always displayed in JMol and only now realize I could have written in JMol script directly and have folded and viewed in the same application. I am now doing so and will present scripts of possible interest to others in the discussion tab for the general amusement.
+
My interest is in protein folding for which I have written software tools in Java over the years. I had always displayed in JMol and only now realize I could have written in JMol script directly and have folded and viewed in the same application. I am now doing so and will present scripts of possible interest to others for the general amusement.
  
The script shown here before that generates polypeptide helices is now available in the Recycling Corner: [[Recycling_Corner/Alpha_Helix_Generator]] so I have removed it from here. In its place is a similar script I wrote that accepts a nucleotide sequence (1 letter encoding: "TACGAAC...GCT" for example) and generates a DNA or RNA single or double helix using the Model Kit:
+
The script shown here before that generates polypeptide helices is now available in the Recycling Corner: [[Recycling_Corner/Alpha_Helix_Generator]] so I have removed it from here. In its place is a similar script (now revised) I wrote that accepts a nucleotide sequence (1 letter encoding: "TACGAAC...GCT" for example) and generates a DNA or RNA single or double helix using the Model Kit:
  
 
<pre>#  POLYMERAZE - Jmol script by Ron Mignery
 
<pre>#  POLYMERAZE - Jmol script by Ron Mignery
#  v1.0 beta    10/31/2013
+
#  v1.1 beta    11/02/2013 for Jmol 13.4
 
#
 
#
 
#  POLYMERAZE takes a string message encoding a nucleotide (nt) sequence
 
#  POLYMERAZE takes a string message encoding a nucleotide (nt) sequence
Line 15: Line 15:
 
#  If prepended with 'R' then RNA is generated instead of DNA
 
#  If prepended with 'R' then RNA is generated instead of DNA
 
#  If prepended with 'S' then a single strand helix is produced
 
#  If prepended with 'S' then a single strand helix is produced
#
+
#   If prepended with 'M' then a mixed helix is produced where the first
Multiple runs append to the previous helix if any unless it is moved
+
strand is DNA and the second RNA
 
#
 
#
 
#  The IUPAC/IUBMB 1 letter code is used:
 
#  The IUPAC/IUBMB 1 letter code is used:
Line 22: Line 22:
  
 
# The following constant values determine the pitch of the helices
 
# The following constant values determine the pitch of the helices
gO4C4C5O5 = -82.3 - 10      # Values from 30-31:B of 3BSE
+
gC5O5PO3 = -27.0
gC4C5O5P = 172.5 + 0        # with the indicated tweaks to make
+
gO5PO3C3 = -117.8
gC5O5PO3 = -44.0 + 9        # it work (sort of - yes, I know
+
gPO3C3C4 = -171.9
gO5PO3C3 = -109.8 + 0      # the B chain P is a bit screwy)
 
gPO3C3C4 = -172.9 - 1      #
 
 
gCHAIN1 = 'A'    # The chain id
 
gCHAIN1 = 'A'    # The chain id
 
gCHAIN2 = 'B'    # The complementary chain id
 
gCHAIN2 = 'B'    # The complementary chain id
  
 
# Lookup 3 letter code from 1 letter code
 
# Lookup 3 letter code from 1 letter code
g3from1 = {"A":" DA", "C":" DC","G":" DG", "T":" DT", "U":" DU"}
+
gNt3from1 = {"A":" DA", "C":" DC", "G":" DG", "T":" DT", "U":" DU"}
gComp = {"A":"T", "C":"G","G":"C", "T":"A", "U":"A"}
+
gNtComp = {"A":"T", "C":"G", "G":"C", "T":"A", "U":"A"}
  
 
# Generate PDB atom record
 
# Generate PDB atom record
 
# Writes gNa or gNb
 
# Writes gNa or gNb
function genAtom(e, aa, i, xyz, comp) {
+
function genAtom(atomname, group, resno, xyz, comp) {
 
     # Fixed column format:
 
     # Fixed column format:
 
     #ATOM    500  O4'  DA B  29      -3.745  7.211  45.474
 
     #ATOM    500  O4'  DA B  29      -3.745  7.211  45.474
     var a =  format("ATOM  %5d %4s %3s ", (comp ? gNb : gNa), e, aa )
+
    while (atomname.size < 3) {
     a +=  format("%s%4d    %8.3f", (comp ? gCHAIN2 : gCHAIN1), i, xyz[1] )           
+
        atomname += " ";
 +
    }
 +
     var a =  format("ATOM  %5d %4s %3s ", (comp ? gNb : gNa), atomname, group )
 +
     a +=  format("%s%4d    %8.3f", (comp ? gCHAIN2 : gCHAIN1), resno, xyz[1] )           
 
     a +=  format("%8.3f%8.3f\n", xyz[2], xyz[3] )
 
     a +=  format("%8.3f%8.3f\n", xyz[2], xyz[3] )
 
     if (comp) gNb++; else gNa++
 
     if (comp) gNb++; else gNa++
Line 52: Line 53:
  
 
     # From constructed nucleotides
 
     # From constructed nucleotides
     var P0 = (comp ? [16.753, 4.551, 8.935] : [0.000, 0.000, 0.000])
+
     var P0 = [0.000, 0.000, 0.000]
     var OP1= (comp ? [17.916, 5.432, 9.209] : [-0.973, 0.363, -1.067])
+
     var OP1= [-0.973,0.363,-1.067]
     var OP2= (comp ? [16.030, 4.050, 10.108] : [0.297, -1.428, 0.272])
+
     var OP2= [0.297,-1.428, 0.272]
     var O5p= (comp ? [16.110, 5.278, 7.954] : [1.351, 0.795, -0.286])
+
     var O5p= [1.351, 0.795,-0.286]
     var C5p= (comp ? [16.536, 5.450, 6.610] : [1.345, 2.211, -0.125])
+
     var C5p= [1.345, 2.211,-0.125]
     var C4p= (comp ? [15.487, 6.211, 5.838] : [2.739, 2.779, -0.195])
+
     var C4p= [2.732, 2.786,-0.255]
     var O4p= (comp ? [14.427, 5.302, 5.454] : [3.469, 2.629, 1.048])
+
     var O4p= [3.413, 2.900, 1.019]
     var C3p= (comp ? [14.830, 7.309, 6.676] : [3.624, 2.197, -1.288])
+
     var C3p= [3.670, 2.020,-1.178]
     var O3p= (comp ? [14.657, 8.490, 5.878] : [4.212, 3.282, -1.983])
+
     var O3p= [4.269, 2.960,-2.051]
     var C2p= (comp ? [13.509, 6.688, 7.119] : [4.692, 1.432, -0.523])
+
     var C2p= [4.717, 1.445,-0.238]
     var O2p= (comp ? [12.530, 7.743, 7.153] : [5.994, 1.461, -1.226])
+
     var O2p= [6.046, 1.365,-0.884]
     var C1p= (comp ? [13.175, 5.739, 5.976] : [4.797, 2.255, 0.746])
+
     var C1p= [4.758, 2.505, 0.846]
 
      
 
      
     var N1ct=(comp ? [12.404, 4.556, 6.401] :  [5.353, 1.551, 1.907])
+
     var N1ct= [5.277, 2.056, 2.143]
     var C2ct=(comp ? [11.423, 4.111, 5.551] : [6.351, 2.183, 2.606])
+
     var C2ct= [6.236, 2.836, 2.740]
     var O2ct=(comp ? [11.137, 4.685, 4.516] : [6.789, 3.274, 2.289])
+
     var O2ct= [6.670, 3.853, 2.230]
     var N3ct=(comp ? [10.784, 2.966, 5.956] :  [6.823, 1.489, 3.692])
+
     var N3ct= [6.674, 2.381, 3.958]
     var C4ct=(comp ? [11.025, 2.241, 7.106] :  [6.404, 0.251, 4.135])
+
     var C4ct= [6.256, 1.245, 4.622]
     var NO4ct=(comp ?[10.382, 1.213, 7.329] :  [6.908, -0.242, 5.146])
+
     var NO4ct [6.726, 0.972, 5.728]
     var C5ct=(comp ? [12.061, 2.780, 7.971] :  [5.360, -0.370, 3.337])
+
     var C5ct= [5.255, 0.455, 3.924]
     var C6ct=(comp ? [12.692, 3.894, 7.575] :  [4.892, 0.307, 2.279])
+
     var C6ct= [4.820, 0.900, 2.737]
     var nC7ct=(comp ?[12.421, 2.090, 9.243] :  [4.862, -1.727, 3.721])
+
     var nC7ct [4.762,-0.811, 4.551]
 
      
 
      
     var N9ag=(comp ? [12.426, 4.545, 6.367] :  [5.333, 1.584, 1.923])
+
     var N9ag= [5.256, 2.091, 2.152]
     var C8ag=(comp ? [12.706, 3.662, 7.382] :  [4.870, 0.450, 2.545])
+
     var C8ag= [4.867, 1.016, 2.913]
     var N7ag=(comp ? [11.856, 2.668, 7.467] :  [5.595, 0.076, 3.570])
+
     var N7ag= [5.532, 0.894, 4.035]
     var C5ag=(comp ? [10.952, 2.911, 6.441] :  [6.608, 1.025, 3.629])
+
     var C5ag= [6.425, 1.959, 4.013]
     var C6ag=(comp ? [9.818, 2.215, 5.997] :  [7.694, 1.194, 4.500])
+
     var C6ag= [7.401, 2.391, 4.922]
     var NO6ag=(comp ?[9.379, 1.083, 6.552] :  [7.955, 0.379, 5.525])
+
     var NO6ag=[7.656, 1.780, 6.081]
     var N1ag=(comp ? [9.137, 2.727, 4.945] :  [8.517, 2.246, 4.283])
+
     var N1ag= [8.118, 3.493, 4.599]
     var C2ag=(comp ? [9.573, 3.862, 4.390] :  [8.259, 3.061, 3.256])
+
     var C2ag= [7.865, 4.104, 3.438]
     var nN2ag=(comp ?[8.847, 4.313, 3.345] :  [9.119, 4.090, 3.100])
+
     var nN2ag [8.616, 5.197, 3.181]
     var N3ag=(comp ? [10.630, 4.605, 4.715] :  [7.265, 3.009, 2.370])
+
     var N3ag= [6.968, 3.796, 2.503]
     var C4ag=(comp ? [11.285, 4.068, 5.759] :  [6.465, 1.956, 2.616])
+
     var C4ag= [6.271, 2.701, 2.856]
 
      
 
      
 
     # Build PDB atom records common to all NTs
 
     # Build PDB atom records common to all NTs
     n3 = g3from1[nt]
+
     n3 = gNt3from1[nt]
 
     if (n3 = "") {
 
     if (n3 = "") {
 
         n3 = " D?"
 
         n3 = " D?"
 
     }
 
     }
    print format("+ %s%d/%d", n3, i, gSeq.count + gResno)
 
 
     var a = genAtom(" P  ", n3, i, P0, comp)
 
     var a = genAtom(" P  ", n3, i, P0, comp)
 
     a += genAtom(" OP1", n3, i, OP1, comp)
 
     a += genAtom(" OP1", n3, i, OP1, comp)
Line 191: Line 191:
 
}
 
}
  
function countAtoms(seq, rna) {
+
function countAtoms(seq, rna, start, finish) {
 
     var ntc = {"A":21, "C":20, "G":22, "T":20, "U":19}
 
     var ntc = {"A":21, "C":20, "G":22, "T":20, "U":19}
 
     var cnt = 0
 
     var cnt = 0
     for (var i = 1; i <= seq.count; i++) {
+
     for (var i = start; i <= finish; i++) {
 
         cnt += (ntc[seq[i]] + (rna ? 1 : 0))
 
         cnt += (ntc[seq[i]] + (rna ? 1 : 0))
 
     }
 
     }
Line 201: Line 201:
  
 
# Generate a helix
 
# Generate a helix
function genHelixStrand(gSeq, reverse, rna, double) {
+
function genHelixStrand(gSeq, reverse, drm, double) {
     var cha = ":A"
+
     var cha = ":" + gCHAIN1
     var chb = ":B"
+
     var chb = ":" + gCHAIN2
 
     var seq = ""
 
     var seq = ""
 
     if (reverse) {
 
     if (reverse) {
 
         for (var i = gSeq.count; i > 0; i--) {
 
         for (var i = gSeq.count; i > 0; i--) {
             seq += gSeq[i]
+
             seq += gSeq[i]%9999%0
 
         }
 
         }
 
     }
 
     }
 
     else {
 
     else {
         seq = gSeq
+
         seq = gSeq%9999%0
 
     }
 
     }
  
     gNa = all.count + 1 # global new N atom index
+
     cSeq = ""
     gNb = (double ? (gNa + countAtoms(seq) + ((gResno > 0) ? 0 : 1)) : 0)
+
    if (double) {
 +
        for (var i = seq.count; i > 0; i--) {
 +
            cSeq += ((seq[i] == 'A') and (dr > 0)) ? "U" : gNtComp[seq[i]]
 +
        }
 +
    }
 +
    var aAtomCount = countAtoms(seq, (drm == 1), 1, seq.count)
 +
    var bAtomCount = countAtoms(cSeq, (drm > 0), 1, cSeq.count)
 +
   
 +
    gNa = 1 # global new P atom index for chain A
 +
     gNb = 0
 +
    if (double) {
 +
        gNb = (aAtomCount + bAtomCount
 +
        - countAtoms(cSeq, (drm>0), cSeq.count, cSeq.count)) # last P in cSeq
 +
    }
 +
    #var bBase = (all.count + countAtoms(seq, (drm > 0), seq.count) + 1)
  
 
     # Find last linkable P if any
 
     # Find last linkable P if any
     gResno = 0    # global pre-existing NT count
+
     var aResno = 1
     var pna = 1    # previous gN
+
     var pNa = 1    # previous gNa
     for (var i = all.count-1; i  > 0; i--) {
+
     for (var i = all.count; i  > 0; i--) {
  
         # If found
+
         # If A strand found at {0,0,0}
 
         if (distance({atomno=i}, {0,0,0})  < 0.1) {
 
         if (distance({atomno=i}, {0,0,0})  < 0.1) {
             if ({atomno=i}.chain == cha[2]) {
+
             if ({atomno=i}.chain == gCHAIN1) {
                 pna = i
+
           
 +
                # Add to existing strand
 +
                echo "Adding to existing strand..."
 +
                 pNa = i
 +
                aResno = {chain=gCHAIN1}.resno.max + 1
 +
                gNa = {chain=gCHAIN1}.atomno.max + 1
 +
                gNb += gNa
 +
               
 +
                # Bump up all B chain atomno and resno
 +
                # KLUDGE to work-around of Jmol's lack of resno rewrite
 +
                savNb = gNb
 +
                gNb = aAtomCount + bAtomCount + gNa
 +
                gA = "data \"append nt\"\n"    # global PDB atom record
 +
                for (j = 1; j <= all.atomno.max; j++) {
 +
                    if ({atomno=j}.chain == gCHAIN2) {
 +
                        gA += genAtom({atomno=j}.atomName, {atomno=j}.group,
 +
                            ({atomno=j}.resno+seq.count+cSeq.count),
 +
                            array({atomno=j}.x, {atomno=j}.y, {atomno=j}.z), true)
 +
                    }
 +
                }
 +
                gA += "end \"append nt\""
 +
                delete @chb
 +
                script inline @{gA} # <== new atoms added here
 +
                gNb = savNb
 +
                break;
 
             }
 
             }
            gResno = {atomno=i}.resno
 
            break;
 
 
         }
 
         }
 
     }
 
     }
  
     # For each nt
+
    var bResno = aResno + seq.count + cSeq.count - 1
 +
   
 +
    var nNa = gNa    # new P
 +
    var nNb = 0#bBase  # new comp P
 +
 
 +
     # For each NT
 
     set appendnew false
 
     set appendnew false
    var nna = gNa    # new P
 
    var nnb = gNb    # new P
 
 
     for (var i = 1; i <= seq.count; i++) {
 
     for (var i = 1; i <= seq.count; i++) {
 +
       
 
         if (seq[i] == "") {
 
         if (seq[i] == "") {
 
             continue
 
             continue
 
         }
 
         }
 
            
 
            
        gA = "data \"append nt\"\n"    # global PDB atom record
 
       
 
 
         # Move polynucleotide O3p to bond distance from new nt P
 
         # Move polynucleotide O3p to bond distance from new nt P
 
         var pO3 = {-0.521, 0.638, 1.234}
 
         var pO3 = {-0.521, 0.638, 1.234}
 
         select all
 
         select all
         if ((i + gResno) > 1) {
+
         if ((i + aResno) > 2) {
             var nO3 =  {atomno=@{pna+8}}.xyz
+
             var nO3 =  {atomno=@{pNa+8}}.xyz
 
             var xyz = @{pO3 - nO3}
 
             var xyz = @{pO3 - nO3}
 
             translateselected @xyz
 
             translateselected @xyz
 
         }
 
         }
 
+
          
         # Else 1st 5' nt so add OP3
 
        else {
 
            var O3n = {pO3n}.xyz
 
            gA += genAtom(" OP3", g3from1[seq[i]], i + gResno, O3n, FALSE)
 
            nna++
 
        }
 
               
 
 
         # Gen NT ==================================================
 
         # Gen NT ==================================================
         gA += genNT(i + gResno, seq[i], rna, FALSE);   # gNa updated
+
        gA = "data \"append nt\"\n"    # global PDB atom record
 +
         gA += genNT(aResno, seq[i], (drm == 1), FALSE); # gNa updated
 
         if (double) {
 
         if (double) {
             gA += genNT(i + gResno + seq.count, gComp[seq[i]], rna, TRUE);   # gNb updated
+
            nNb = gNb
 +
            var nti = cSeq.count-i+1
 +
             gA += genNT(bResno, cSeq[nti], (drm > 0), TRUE); # gNb++
 +
            if (i > 0) {
 +
                gNb -= countAtoms(cSeq, (drm>0), nti-1, nti)
 +
            }
 
         }
 
         }
 
         gA += "end \"append nt\""
 
         gA += "end \"append nt\""
 
         script inline @{gA} # <== new atoms added here
 
         script inline @{gA} # <== new atoms added here
 
+
       
         # First shape up the comp side
+
         # Flip comp to comp strand
 
         if (double) {
 
         if (double) {
             select (@chb and (atomno < @{nnb + 6}) && (atomno >= nnb))
+
             select @{"" + bResno + chb}
             setDihedral(nnb+6, nnb+5, nnb+4, nnb+3, gO4C4C5O5)
+
             var v1={8.238, 2.809, 6.004}
             select selected and (atomno != @{nnb + 5})
+
             var v2={8.461, 4.646, 4.125}
             setDihedral(nnb+5, nnb+4, nnb+3, nnb, gC4C5O5P)
+
             rotateSelected @v2 @v1 180.0
 
         }
 
         }
         # Adjust link dihedrals of the new
+
          
        select (@cha and (atomno > @{nna+4}) or (@chb and (atomno >= nnb)))
+
        # If any older NTs
        setDihedral(nna+3, nna+4, nna+5, nna+6, gO4C4C5O5)
+
        if ((i + aResno) > 2) {
        setDihedral(nna, nna+3, nna+4, nna+5, gC4C5O5P)
+
            # Set the angles between the new NT and the old NTs
   
+
            select (@cha and (atomno < nNa) or (@chb and (resno != bResno)))
        # If any older
+
            setAngle(nNa, pNa+8, pNa+7, 120.0)
        if (i > 1) {
+
 
            # Now move the old
+
             select (@cha and (atomno < @{nNa+3}) or (@chb and (resno != bResno)))
             select (@cha and (atomno < nna) or (@chb and (atomno < nnb)))
+
             setDihedral(nNa+4, nNa+3, nNa, pNa+8, gC5O5PO3)
             setAngle(nna, pna+8, pna+7, 120.0)
 
 
              
 
              
             select (@cha and (atomno < @{nna+3}) or (@chb and (atomno < nnb)))
+
             select (@cha and (atomno < nNa) or (@chb and (resno != bResno)))
             setDihedral(nna+4, nna+3, nna, pna+8, gC5O5PO3)
+
             setDihedral(nNa+3, nNa, pNa+8, pNa+7, gO5PO3C3)
 
              
 
              
            select (@cha and (atomno < nna) or (@chb and (atomno < nnb)))
+
             setDihedral(nNa, pNa+8, pNa+7, pNa+5, gPO3C3C4)
            setDihedral(nna+3, nna, pna+8, pna+7, gO5PO3C3)
 
           
 
             setDihedral(nna, pna+8, pna+7, pna+5, gPO3C3C4)
 
 
         }
 
         }
 
          
 
          
 
         # Step new and previous N
 
         # Step new and previous N
         pna = nna; pnb = nnb
+
         aResno++; bResno--
         nna = gNa; nnb = gNb
+
        pNa = nNa; pnb = nNb
 +
         nNa = gNa; nNb = gNb
 
     }
 
     }
 
      
 
      
Line 310: Line 344:
 
# Generate a helix or two
 
# Generate a helix or two
 
function genHelix(gSeq) {
 
function genHelix(gSeq) {
var single = FALSE
+
    var single = FALSE
var reverse = FALSE
+
    var reverse = FALSE
var rna = FALSE
+
    var drm = 0
var done = FALSE
+
    var done = FALSE
 +
    gSeq = gSeq%9999%0
 +
    print format ("Seq=%s", gSeq)
 +
   
 
     if (gSeq[2] == ':') {
 
     if (gSeq[2] == ':') {
 
         gCHAIN1 = gSeq[1]
 
         gCHAIN1 = gSeq[1]
Line 336: Line 373:
 
         }
 
         }
 
         else if (gSeq[1] == 'R') {
 
         else if (gSeq[1] == 'R') {
             rna = TRUE;
+
             drm = 1;
 +
            done = FALSE;
 +
        }
 +
        else if (gSeq[1] == 'M') {
 +
            drm = 2;
 
             done = FALSE;
 
             done = FALSE;
 
         }
 
         }
Line 344: Line 385:
 
         }
 
         }
 
     }
 
     }
    print format ("Sequence=%s single=%s reverse=%s", gSeq, single, reverse)
 
    print format ("rna=%s", rna)
 
 
      
 
      
     # Gen first strand
+
     # Generate
     genHelixStrand(gSeq, reverse, rna, single ? FALSE : TRUE)
+
     genHelixStrand(gSeq, reverse, drm, single ? FALSE : TRUE)
  
 
}
 
}
Line 356: Line 395:
  
 
# Get the sequence from the user
 
# Get the sequence from the user
gSeq = prompt("Enter NT sequence (ACGTU)", "")%9999%0
+
gSeq = prompt("Enter NT sequence (<3RSM>ACGTU)", "")%9999%0
 
if (gSeq.count > 0) {
 
if (gSeq.count > 0) {
 
     genHelix(gSeq)
 
     genHelix(gSeq)
 
}
 
}
 
</pre>
 
</pre>
I thought adapting the Ribozome script to polynucleotides would be easy since there are only four types rather than 20. I was not easy and this script has its problems. With five rotors in a row for each nucleotide, finding correct values for the bond angles was beyond me. If anyone can tweak them better, please do and let me know. Regardless I plan to develop some more utility scripts to make that process easier and hope to update this script in the hopefully not too distant future.
 

Revision as of 18:48, 4 November 2013

My interest is in protein folding for which I have written software tools in Java over the years. I had always displayed in JMol and only now realize I could have written in JMol script directly and have folded and viewed in the same application. I am now doing so and will present scripts of possible interest to others for the general amusement.

The script shown here before that generates polypeptide helices is now available in the Recycling Corner: Recycling_Corner/Alpha_Helix_Generator so I have removed it from here. In its place is a similar script (now revised) I wrote that accepts a nucleotide sequence (1 letter encoding: "TACGAAC...GCT" for example) and generates a DNA or RNA single or double helix using the Model Kit:

#   POLYMERAZE - Jmol script by Ron Mignery
#   v1.1 beta    11/02/2013 for Jmol 13.4
#
#   POLYMERAZE takes a string message encoding a nucleotide (nt) sequence
#   and generates a corresponding double helix one nt at a time from the
#   5' terminus to the 3' terminus rotating the emerging helix as it goes.
#
#   The message is a string entered by the user at a prompt.
#   It may be typed in or pasted in and be of any length
#   If prepended with '3' then the string is considered as 3' to 5'
#   If prepended with 'R' then RNA is generated instead of DNA
#   If prepended with 'S' then a single strand helix is produced
#   If prepended with 'M' then a mixed helix is produced where the first
#   strand is DNA and the second RNA
#
#   The IUPAC/IUBMB 1 letter code is used:
#   A=Adenine C=Cytosine G=Guanine T=Thymine U=Uracil

# The following constant values determine the pitch of the helices
gC5O5PO3 = -27.0
gO5PO3C3 = -117.8
gPO3C3C4 = -171.9
gCHAIN1 = 'A'    # The chain id
gCHAIN2 = 'B'    # The complementary chain id

# Lookup 3 letter code from 1 letter code
gNt3from1 = {"A":" DA", "C":" DC", "G":" DG", "T":" DT", "U":" DU"}
gNtComp = {"A":"T", "C":"G", "G":"C", "T":"A", "U":"A"}

# Generate PDB atom record
# Writes gNa or gNb
function genAtom(atomname, group, resno, xyz, comp) {
    # Fixed column format:
    #ATOM    500  O4'  DA B  29      -3.745   7.211  45.474
    while (atomname.size < 3) {
        atomname += " ";
    }
    var a =  format("ATOM  %5d %4s %3s ", (comp ? gNb : gNa), atomname, group )
    a +=  format("%s%4d    %8.3f", (comp ? gCHAIN2 : gCHAIN1), resno, xyz[1] )          
    a +=  format("%8.3f%8.3f\n", xyz[2], xyz[3] )
    if (comp) gNb++; else gNa++
        
    return a
};

# Generate a PDB nucleotide record set
# Calls genAtom that writes gNa or gNb
function genNT(i, nt, rna, comp) {

    # From constructed nucleotides
    var P0 =  [0.000, 0.000, 0.000]
    var OP1=  [-0.973,0.363,-1.067]
    var OP2=  [0.297,-1.428, 0.272]
    var O5p=  [1.351, 0.795,-0.286]
    var C5p=  [1.345, 2.211,-0.125]
    var C4p=  [2.732, 2.786,-0.255]
    var O4p=  [3.413, 2.900, 1.019]
    var C3p=  [3.670, 2.020,-1.178]
    var O3p=  [4.269, 2.960,-2.051]
    var C2p=  [4.717, 1.445,-0.238]
    var O2p=  [6.046, 1.365,-0.884]
    var C1p=  [4.758, 2.505, 0.846]
    
    var N1ct= [5.277, 2.056, 2.143]
    var C2ct= [6.236, 2.836, 2.740]
    var O2ct= [6.670, 3.853, 2.230]
    var N3ct= [6.674, 2.381, 3.958]
    var C4ct= [6.256, 1.245, 4.622]
    var NO4ct [6.726, 0.972, 5.728]
    var C5ct= [5.255, 0.455, 3.924]
    var C6ct= [4.820, 0.900, 2.737]
    var nC7ct [4.762,-0.811, 4.551]
    
    var N9ag= [5.256, 2.091, 2.152]
    var C8ag= [4.867, 1.016, 2.913]
    var N7ag= [5.532, 0.894, 4.035]
    var C5ag= [6.425, 1.959, 4.013]
    var C6ag= [7.401, 2.391, 4.922]
    var NO6ag=[7.656, 1.780, 6.081]
    var N1ag= [8.118, 3.493, 4.599]
    var C2ag= [7.865, 4.104, 3.438]
    var nN2ag [8.616, 5.197, 3.181]
    var N3ag= [6.968, 3.796, 2.503]
    var C4ag= [6.271, 2.701, 2.856]
    
    # Build PDB atom records common to all NTs
    n3 = gNt3from1[nt]
    if (n3 = "") {
        n3 = " D?"
    }
    var a = genAtom(" P  ", n3, i, P0, comp)
    a += genAtom(" OP1", n3, i, OP1, comp)
    a += genAtom(" OP2", n3, i, OP2, comp)
    a += genAtom(" O5'", n3, i, O5p, comp)
    a += genAtom(" C5'", n3, i, C5p, comp)
    a += genAtom(" C4'", n3, i, C4p, comp)
    a += genAtom(" O4'", n3, i, O4p, comp)
    a += genAtom(" C3'", n3, i, C3p, comp)
    a += genAtom(" O3'", n3, i, O3p, comp)
    a += genAtom(" C2'", n3, i, C2p, comp)
    a += genAtom(" C1'", n3, i, C1p, comp)
    if (rna) {
        a += genAtom(" O2'", n3, i, O2p, comp)
    }

    # Now add NT specific atom records
    switch (nt) {
    case 'A' :
        a += genAtom(" N9 ", n3, i, N9ag, comp)
        a += genAtom(" C8 ", n3, i, C8ag, comp)
        a += genAtom(" N7 ", n3, i, N7ag, comp)
        a += genAtom(" C5 ", n3, i, C5ag, comp)
        a += genAtom(" C6 ", n3, i, C6ag, comp)
        a += genAtom(" N6 ", n3, i, NO6ag, comp)
        a += genAtom(" N1 ", n3, i, N1ag, comp)
        a += genAtom(" C2 ", n3, i, C2ag, comp)
        a += genAtom(" N3 ", n3, i, N3ag, comp)
        a += genAtom(" C4 ", n3, i, C4ag, comp)
        break;
    case 'C' :
        a += genAtom(" N1 ", n3, i, N1ct, comp)
        a += genAtom(" C2 ", n3, i, C2ct, comp)
        a += genAtom(" O2 ", n3, i, O2ct, comp)
        a += genAtom(" N3 ", n3, i, N3ct, comp)
        a += genAtom(" C4 ", n3, i, C4ct, comp)
        a += genAtom(" N4 ", n3, i, NO4ct, comp)
        a += genAtom(" C5 ", n3, i, C5ct, comp)
        a += genAtom(" C6 ", n3, i, C6ct, comp)
        break;
    case 'G' :
        a += genAtom(" N9 ", n3, i, N9ag, comp)
        a += genAtom(" C8 ", n3, i, C8ag, comp)
        a += genAtom(" N7 ", n3, i, N7ag, comp)
        a += genAtom(" C5 ", n3, i, C5ag, comp)
        a += genAtom(" C6 ", n3, i, C6ag, comp)
        a += genAtom(" O6 ", n3, i, NO6ag, comp)
        a += genAtom(" N1 ", n3, i, N1ag, comp)
        a += genAtom(" C2 ", n3, i, C2ag, comp)
        a += genAtom(" N2 ", n3, i, nN2ag, comp)
        a += genAtom(" N3 ", n3, i, N3ag, comp)
        a += genAtom(" C4 ", n3, i, C4ag, comp)
        break;
    case 'T' :
        a += genAtom(" N1 ", n3, i, N1ct, comp)
        a += genAtom(" C2 ", n3, i, C2ct, comp)
        a += genAtom(" O2 ", n3, i, O2ct, comp)
        a += genAtom(" N3 ", n3, i, N3ct, comp)
        a += genAtom(" C4 ", n3, i, C4ct, comp)
        a += genAtom(" O4 ", n3, i, NO4ct, comp)
        a += genAtom(" C5 ", n3, i, C5ct, comp)
        a += genAtom(" C6 ", n3, i, C6ct, comp)
        a += genAtom(" C7 ", n3, i, nC7ct, comp)
        break;
    case 'U' :
        a += genAtom(" N1 ", n3, i, N1ct, comp)
        a += genAtom(" C2 ", n3, i, C2ct, comp)
        a += genAtom(" O2 ", n3, i, O2ct, comp)
        a += genAtom(" N3 ", n3, i, N3ct, comp)
        a += genAtom(" C4 ", n3, i, C4ct, comp)
        a += genAtom(" O4 ", n3, i, NO4ct, comp)
        a += genAtom(" C5 ", n3, i, C5ct, comp)
        a += genAtom(" C6 ", n3, i, C6ct, comp)
        break;
    default :
        break;
    }

    return a
};

# Rotate a1 on a2 in the plane of a1, a2 and a3 to the given angle
# a1 and all connected except by a2 must be selected 
function setAngle (a1, a2, a3, toangle) {
    var v1={atomno = a1}.xyz - {atomno = a2}.xyz
    var v2={atomno = a3}.xyz - {atomno = a2}.xyz
    var axis = cross(v1, v2) + {atomno = a2}.xyz
    var curangle =  angle({atomno=a1}, {atomno=a2}, {atomno=a3})
    rotateselected @axis {atomno = a2} @{curangle-toangle}
}

# Set the dihedral to the given angle
# a1 (or a4) and all connected except by a2 (or a3) must be selected 
# If selected < unselected ==> a2 < a3 and vice versa
function setDihedral (a1, a2, a3, a4, toangle) {
    var curangle =  angle({atomno=a1}, {atomno=a2}, {atomno=a3}, {atomno=a4})
    rotateselected {atomno=a2} {atomno=a3} @{toangle-curangle}
}

function countAtoms(seq, rna, start, finish) {
    var ntc = {"A":21, "C":20, "G":22, "T":20, "U":19}
    var cnt = 0
    for (var i = start; i <= finish; i++) {
        cnt += (ntc[seq[i]] + (rna ? 1 : 0))
    }
    return cnt
}

# Generate a helix
function genHelixStrand(gSeq, reverse, drm, double) {
    var cha = ":" + gCHAIN1
    var chb = ":" + gCHAIN2
    var seq = ""
    if (reverse) {
        for (var i = gSeq.count; i > 0; i--) {
            seq += gSeq[i]%9999%0
        }
    }
    else {
        seq = gSeq%9999%0
    }

    cSeq = ""
    if (double) {
        for (var i = seq.count; i > 0; i--) {
            cSeq += ((seq[i] == 'A') and (dr > 0)) ? "U" : gNtComp[seq[i]]
        }
    }
    var aAtomCount = countAtoms(seq, (drm == 1), 1, seq.count)
    var bAtomCount = countAtoms(cSeq, (drm > 0), 1, cSeq.count)
    
    gNa = 1 # global new P atom index for chain A
    gNb = 0
    if (double) {
        gNb = (aAtomCount + bAtomCount
         - countAtoms(cSeq, (drm>0), cSeq.count, cSeq.count)) # last P in cSeq
    }
    #var bBase = (all.count + countAtoms(seq, (drm > 0), seq.count) + 1)

    # Find last linkable P if any
    var aResno = 1
    var pNa = 1    # previous gNa
    for (var i = all.count; i  > 0; i--) {

        # If A strand found at {0,0,0}
        if (distance({atomno=i}, {0,0,0})  < 0.1) {
            if ({atomno=i}.chain == gCHAIN1) {
            
                # Add to existing strand
                echo "Adding to existing strand..."
                pNa = i
                aResno = {chain=gCHAIN1}.resno.max + 1
                gNa = {chain=gCHAIN1}.atomno.max + 1
                gNb += gNa
                
                # Bump up all B chain atomno and resno
                # KLUDGE to work-around of Jmol's lack of resno rewrite
                savNb = gNb
                gNb = aAtomCount + bAtomCount + gNa
                gA = "data \"append nt\"\n"    # global PDB atom record
                for (j = 1; j <= all.atomno.max; j++) {
                    if ({atomno=j}.chain == gCHAIN2) {
                        gA += genAtom({atomno=j}.atomName, {atomno=j}.group,
                            ({atomno=j}.resno+seq.count+cSeq.count),
                            array({atomno=j}.x, {atomno=j}.y, {atomno=j}.z), true)
                    } 
                }
                gA += "end \"append nt\""
                delete @chb
                script inline @{gA} # <== new atoms added here
                gNb = savNb
                break;
            }
        }
    }

    var bResno = aResno + seq.count + cSeq.count - 1
    
    var nNa = gNa    # new P
    var nNb = 0#bBase  # new comp P

    # For each NT
    set appendnew false
    for (var i = 1; i <= seq.count; i++) {
        
        if (seq[i] == "") {
            continue
        }
           
        # Move polynucleotide O3p to bond distance from new nt P
        var pO3 = {-0.521, 0.638, 1.234}
        select all
        if ((i + aResno) > 2) {
            var nO3 =  {atomno=@{pNa+8}}.xyz
            var xyz = @{pO3 - nO3}
            translateselected @xyz
        }
        
        # Gen NT ==================================================
        gA = "data \"append nt\"\n"    # global PDB atom record
        gA += genNT(aResno, seq[i], (drm == 1), FALSE); # gNa updated
        if (double) {
            nNb = gNb
            var nti = cSeq.count-i+1
            gA += genNT(bResno, cSeq[nti], (drm > 0), TRUE); # gNb++
            if (i > 0) {
                gNb -= countAtoms(cSeq, (drm>0), nti-1, nti)
            }
        }
        gA += "end \"append nt\""
        script inline @{gA} # <== new atoms added here
        
        # Flip comp to comp strand
        if (double) {
            select @{"" + bResno + chb}
            var v1={8.238, 2.809, 6.004}
            var v2={8.461, 4.646, 4.125}
            rotateSelected @v2 @v1 180.0
        }
        
        # If any older NTs
        if ((i + aResno) > 2) {
            # Set the angles between the new NT and the old NTs
            select (@cha and (atomno < nNa) or (@chb and (resno != bResno)))
            setAngle(nNa, pNa+8, pNa+7, 120.0)

            select (@cha and (atomno < @{nNa+3}) or (@chb and (resno != bResno)))
            setDihedral(nNa+4, nNa+3, nNa, pNa+8, gC5O5PO3)
            
            select (@cha and (atomno < nNa) or (@chb and (resno != bResno)))
            setDihedral(nNa+3, nNa, pNa+8, pNa+7, gO5PO3C3)
            
            setDihedral(nNa, pNa+8, pNa+7, pNa+5, gPO3C3C4)
        }
        
        # Step new and previous N
        aResno++; bResno--
        pNa = nNa; pnb = nNb
        nNa = gNa; nNb = gNb
    }
    
    # Make the nucleotide bonds
    connect
    
    # Clean up
    select all
    print format("%d atoms generated for chain %s", gNa+gNb,
        (comp ? gCHAIN2 : gCHAIN1))
}

# Generate a helix or two
function genHelix(gSeq) {
    var single = FALSE
    var reverse = FALSE
    var drm = 0
    var done = FALSE
    gSeq = gSeq%9999%0
    print format ("Seq=%s", gSeq)
    
    if (gSeq[2] == ':') {
        gCHAIN1 = gSeq[1]
        gSeq[1] = ' '; gSeq[2] = ' '
        gSeq = gSeq%0
    }
    else if (gSeq[3] == ':') {
        gCHAIN1 = gSeq[1]
        gCHAIN2 = gSeq[2]
        gSeq[1] = ' '; gSeq[2] = ' '; gSeq[3] = ' '
        gSeq = gSeq%0
    }
    while (done == FALSE) {
        done = TRUE;
        if (gSeq[1] == 'S') {
            single = TRUE;
            done = FALSE;
        }
        else if (gSeq[1] == '3') {
            reverse = TRUE;
            done = FALSE;
        }
        else if (gSeq[1] == 'R') {
            drm = 1;
            done = FALSE;
        }
        else if (gSeq[1] == 'M') {
            drm = 2;
            done = FALSE;
        }
        if (done == FALSE) {
            gSeq[1] = ' '
            gSeq = gSeq%0
        }
    }
    
    # Generate
    genHelixStrand(gSeq, reverse, drm, single ? FALSE : TRUE)

}

# ==============================================
echo Generating Alpha Helix

# Get the sequence from the user
gSeq = prompt("Enter NT sequence (<3RSM>ACGTU)", "")%9999%0
if (gSeq.count > 0) {
    genHelix(gSeq)
}

Contributors

Remig