Skip to content

Commit

Permalink
Merge pull request #136 from UC-Davis-molecular-computing/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
dave-doty authored Sep 10, 2020
2 parents 653d450 + db82d47 commit a06ce07
Show file tree
Hide file tree
Showing 7 changed files with 2,850 additions and 645 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ If you find scadnano useful in a scientific project, please cite its associated
> scadnano: A browser-based, scriptable tool for designing DNA nanostructures.
David Doty, Benjamin L Lee, and Tristan Stérin.
DNA 2020: *Proceedings of the 26th International Conference on DNA Computing and Molecular Programming*
[ [paper](https://arxiv.org/abs/2005.11841) | [BibTeX](https://web.cs.ucdavis.edu/~doty/papers/scadnano.bib) ]
[ [paper](https://doi.org/10.4230/LIPIcs.DNA.2020.9) | [BibTeX](https://web.cs.ucdavis.edu/~doty/papers/scadnano.bib) ]


## Overview
Expand Down
45 changes: 45 additions & 0 deletions examples/names_domains_strands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import scadnano as sc


def create_design() -> sc.Design:
num_domains = 9
helices = [sc.Helix(max_offset=(num_domains) * 8)]
design: sc.Design = sc.Design(helices=helices, strands=[], grid=sc.square)
design.strand(0, (num_domains - 1) * 8) \
.move(-8).with_domain_name('domain 8*') \
.move(-8).with_domain_name('domain 7*') \
.move(-8).with_domain_name('domain 6*') \
.move(-8).with_domain_name('domain 5*') \
.move(-8).with_domain_name('domain 4*') \
.move(-8).with_domain_name('domain 3*') \
.move(-8).with_domain_name('domain 2*') \
.move(-8).with_domain_name('domain 1*') \
.with_name('bottom strand')

# domain names match
design.strand(0, 0).move(8).with_domain_name('domain 1').with_name('top strand 1')

# domains are aligns but names mismatch
design.strand(0, 8).move(8).with_domain_name('domain 50').with_name('top strand 2')

# domain names match
design.strand(0, 16).move(8).with_domain_name('domain 3').with_name('top strand 3')

# domain names are the same, not complementary
design.strand(0, 24).move(8).with_domain_name('domain 4*').with_name('top strand 4')

# domain names match but domains are mis-aligned
design.strand(0, 32).move(9).with_domain_name('domain 5').with_name('top strand 5')

# domain names match but domains are mis-aligned
design.strand(0, 48).move(7).with_domain_name('domain 7').with_name('top strand 7')

# no overlap so no mismatch
design.strand(0, 64).move(8).with_domain_name('domain 9').with_name('top strand 9')

return design


if __name__ == '__main__':
design = create_design()
design.write_scadnano_file(directory='output_designs')
72 changes: 72 additions & 0 deletions examples/output_designs/names_domains_strands.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"version": "0.12.0",
"grid": "square",
"helices": [
{"grid_position": [0, 0]}
],
"strands": [
{
"name": "bottom strand",
"color": "#f74308",
"domains": [
{"name": "domain 8*", "helix": 0, "forward": false, "start": 56, "end": 64},
{"name": "domain 7*", "helix": 0, "forward": false, "start": 48, "end": 56},
{"name": "domain 6*", "helix": 0, "forward": false, "start": 40, "end": 48},
{"name": "domain 5*", "helix": 0, "forward": false, "start": 32, "end": 40},
{"name": "domain 4*", "helix": 0, "forward": false, "start": 24, "end": 32},
{"name": "domain 3*", "helix": 0, "forward": false, "start": 16, "end": 24},
{"name": "domain 2*", "helix": 0, "forward": false, "start": 8, "end": 16},
{"name": "domain 1*", "helix": 0, "forward": false, "start": 0, "end": 8}
]
},
{
"name": "top strand 1",
"color": "#57bb00",
"domains": [
{"name": "domain 1", "helix": 0, "forward": true, "start": 0, "end": 8}
]
},
{
"name": "top strand 2",
"color": "#888888",
"domains": [
{"name": "domain 50", "helix": 0, "forward": true, "start": 8, "end": 16}
]
},
{
"name": "top strand 3",
"color": "#32b86c",
"domains": [
{"name": "domain 3", "helix": 0, "forward": true, "start": 16, "end": 24}
]
},
{
"name": "top strand 4",
"color": "#333333",
"domains": [
{"name": "domain 4*", "helix": 0, "forward": true, "start": 24, "end": 32}
]
},
{
"name": "top strand 5",
"color": "#320096",
"domains": [
{"name": "domain 5", "helix": 0, "forward": true, "start": 32, "end": 41}
]
},
{
"name": "top strand 7",
"color": "#03b6a2",
"domains": [
{"name": "domain 7", "helix": 0, "forward": true, "start": 48, "end": 55}
]
},
{
"name": "top strand 9",
"color": "#7300de",
"domains": [
{"name": "domain 9", "helix": 0, "forward": true, "start": 64, "end": 72}
]
}
]
}
61 changes: 31 additions & 30 deletions scadnano/origami_rectangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
The :mod:`origami_rectangle` module defines the function :py:func:`origami_rectangle.create` for creating a DNA origami rectangle
using the :mod:`scadnano` module.
"""

from dataclasses import dataclass, field

from typing import List, Union, cast
# from . import scadnano as sc
import scadnano as sc
from enum import Enum, auto

#TODO: write version of origami_rectangle.create that uses add_nick and add_crossover.

# TODO: write version of origami_rectangle.create that uses add_nick and add_crossover.

class NickPattern(Enum):
"""Represents options for where to place nicks between staples."""
Expand Down Expand Up @@ -62,12 +61,11 @@ class NickPattern(Enum):
CURRENTLY UNSUPPORTED."""



def create(*, num_helices: int, num_cols: int, assign_seq: bool = True, seam_left_column=-1,
def create(*, num_helices: int, num_cols: int, assign_seq: bool = True, seam_left_column: int = -1,
nick_pattern: NickPattern = NickPattern.staggered,
twist_correction_deletion_spacing: int = 0, twist_correction_start_col: int = 1,
twist_correction_deletion_offset=-1,
num_flanking_columns: int = 1, num_flanking_helices=0,
twist_correction_deletion_offset: int = -1,
num_flanking_columns: int = 1, num_flanking_helices: int = 0,
custom_scaffold: str = None, edge_staples: bool = True,
scaffold_nick_offset: int = -1, use_idt_defaults: bool = False) -> sc.Design:
"""
Expand Down Expand Up @@ -234,7 +232,7 @@ def create(*, num_helices: int, num_cols: int, assign_seq: bool = True, seam_lef
scaffold = _create_scaffold(offset_start, offset_end, offset_mid, num_helices, num_flanking_helices,
scaffold_nick_offset)
staples = _create_staples(offset_start, offset_end, offset_mid, num_helices, num_flanking_helices,
num_cols, nick_pattern, edge_staples, use_idt_defaults)
num_cols, nick_pattern, edge_staples)

design = sc.Design(helices=helices, strands=[scaffold] + staples, grid=sc.square)

Expand Down Expand Up @@ -262,15 +260,15 @@ def create(*, num_helices: int, num_cols: int, assign_seq: bool = True, seam_lef
BASES_PER_COLUMN = 16


def _create_helices(num_helices: int, num_bases_per_helix: int):
def _create_helices(num_helices: int, num_bases_per_helix: int) -> List[sc.Helix]:
return [sc.Helix(max_offset=num_bases_per_helix) for _ in range(num_helices)]


def _create_scaffold(offset_start: int, offset_end: int, offset_mid: int, num_helices: int,
num_flanking_helices: int, scaffold_nick_offset: int):
num_flanking_helices: int, scaffold_nick_offset: int) -> sc.Strand:
# top domain is continguous
top_domain = sc.Domain(helix=0 + num_flanking_helices, forward=True,
start=offset_start, end=offset_end)
start=offset_start, end=offset_end)
domains_left = []
domains_right = []
if scaffold_nick_offset < 0:
Expand All @@ -281,32 +279,33 @@ def _create_scaffold(offset_start: int, offset_end: int, offset_mid: int, num_he
center_offset = offset_mid if helix < num_helices + num_flanking_helices - 1 else scaffold_nick_offset
forward = (helix % 2 == num_flanking_helices % 2)
left_domain = sc.Domain(helix=helix, forward=forward,
start=offset_start, end=center_offset)
start=offset_start, end=center_offset)
right_domain = sc.Domain(helix=helix, forward=forward,
start=center_offset, end=offset_end)
start=center_offset, end=offset_end)
domains_left.append(left_domain)
domains_right.append(right_domain)
domains_left.reverse()
domains = domains_left + [top_domain] + domains_right
domains = cast(List[Union[sc.Domain, sc.Loopout]], # noqa
domains_left + [top_domain] + domains_right) # type: ignore
return sc.Strand(domains=domains, color=sc.default_scaffold_color, is_scaffold=True)


def _create_staples(offset_start: int, offset_end: int, offset_mid: int, num_helices: int,
num_flanking_helices: int, num_cols: int,
nick_pattern: NickPattern, edge_staples, idt: bool):
nick_pattern: NickPattern, edge_staples: bool) -> List[sc.Strand]:
if edge_staples:
left_edge_staples = _create_left_edge_staples(offset_start, num_helices, num_flanking_helices, idt)
right_edge_staples = _create_right_edge_staples(offset_end, num_helices, num_flanking_helices, idt)
left_edge_staples = _create_left_edge_staples(offset_start, num_helices, num_flanking_helices)
right_edge_staples = _create_right_edge_staples(offset_end, num_helices, num_flanking_helices)
else:
left_edge_staples = []
right_edge_staples = []
seam_staples = _create_seam_staples(offset_mid, num_helices, num_flanking_helices, idt)
seam_staples = _create_seam_staples(offset_mid, num_helices, num_flanking_helices)
inner_staples = _create_inner_staples(offset_start, offset_end, offset_mid, num_helices,
num_flanking_helices, num_cols, nick_pattern, idt)
num_flanking_helices, num_cols, nick_pattern)
return left_edge_staples + right_edge_staples + seam_staples + inner_staples


def _create_seam_staples(offset_mid: int, num_helices: int, num_flanking_helices: int, idt: bool):
def _create_seam_staples(offset_mid: int, num_helices: int, num_flanking_helices: int) -> List[sc.Strand]:
staples = []
crossover_left = offset_mid - BASES_PER_COLUMN
crossover_right = offset_mid + BASES_PER_COLUMN
Expand Down Expand Up @@ -339,7 +338,8 @@ def _create_seam_staples(offset_mid: int, num_helices: int, num_flanking_helices
return [first_staple] + staples + [last_staple]


def _create_left_edge_staples(offset_start: int, num_helices: int, num_flanking_helices: int, idt: bool):
def _create_left_edge_staples(offset_start: int, num_helices: int,
num_flanking_helices: int) -> List[sc.Strand]:
staples = []
crossover_right = offset_start + BASES_PER_COLUMN
for helix in range(0 + num_flanking_helices, num_helices + num_flanking_helices, 2):
Expand All @@ -353,7 +353,8 @@ def _create_left_edge_staples(offset_start: int, num_helices: int, num_flanking_
return staples


def _create_right_edge_staples(offset_end: int, num_helices: int, num_flanking_helices: int, idt: bool):
def _create_right_edge_staples(offset_end: int, num_helices: int,
num_flanking_helices: int) -> List[sc.Strand]:
staples = []
crossover_left = offset_end - BASES_PER_COLUMN
for helix in range(0 + num_flanking_helices, num_helices + num_flanking_helices, 2):
Expand All @@ -369,7 +370,7 @@ def _create_right_edge_staples(offset_end: int, num_helices: int, num_flanking_h

def _create_inner_staples(offset_start: int, offset_end: int, offset_mid: int, num_helices: int,
num_flanking_helices: int, num_cols: int,
nick_pattern: NickPattern, idt: bool):
nick_pattern: NickPattern) -> List[sc.Strand]:
if nick_pattern is not NickPattern.staggered:
raise NotImplementedError("Currently can only handle staggered nick pattern")
# if ((num_cols - 4) // 2) % 2 != 0:
Expand Down Expand Up @@ -434,11 +435,11 @@ def _create_inner_staples(offset_start: int, offset_end: int, offset_mid: int, n
return staples


def add_deletion_in_range(design: sc.Design, helix: int, start: int, end: int, deletion_offset: int):
#Inserts deletion somewhere in given range.
def add_deletion_in_range(design: sc.Design, helix: int, start: int, end: int, deletion_offset: int) -> None:
# Inserts deletion somewhere in given range.

#`offset` is the relative offset within a column at which to put the deletions.
#If negative, chooses first available offset.
# `offset` is the relative offset within a column at which to put the deletions.
# If negative, chooses first available offset.
candidate_offsets = []
for candidate_deletion_offset in range(start, end):
if valid_deletion_offset(design, helix, candidate_deletion_offset):
Expand All @@ -455,7 +456,7 @@ def add_deletion_in_range(design: sc.Design, helix: int, start: int, end: int, d
design.add_deletion(helix, deletion_absolute_offset)


def valid_deletion_offset(design: sc.Design, helix: int, offset: int):
def valid_deletion_offset(design: sc.Design, helix: int, offset: int) -> bool:
domains_at_offset = design.domains_at(helix, offset)
if len(domains_at_offset) > 2:
raise ValueError(f'Invalid Design; more than two Substrands found at '
Expand All @@ -482,7 +483,7 @@ def add_twist_correction_deletions(design: sc.Design,
deletion_offset: int,
num_helices: int,
num_cols: int,
num_flanking_helices: int):
num_flanking_helices: int) -> None:
for col in range(deletion_start_col, num_cols):
col_start = offset_start + col * BASES_PER_COLUMN
col_end = offset_start + (col + 1) * BASES_PER_COLUMN
Expand Down
Loading

0 comments on commit a06ce07

Please sign in to comment.