Skip to content

Commit

Permalink
Merge pull request #237 from UC-Davis-molecular-computing/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
dave-doty authored Aug 17, 2022
2 parents 93978f2 + badd6b4 commit 724a4a8
Show file tree
Hide file tree
Showing 12 changed files with 1,695 additions and 223 deletions.
42 changes: 29 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ If you find scadnano useful in a scientific project, please cite its associated
* [Installation](#installation)
* [Example](#example)
* [Abbreviated syntax with chained methods](#abbreviated-syntax-with-chained-methods)
* [StrandBuilder object for iteratively building up strands with many domains](#strandbuilder-object-for-iteratively-building-up-strands-with-many-domains)
* [Tutorial](#tutorial)
* [API documentation](#api-documentation)
* [Other examples](#other-examples)
Expand All @@ -36,7 +37,7 @@ If you find scadnano useful in a scientific project, please cite its associated

This package is used to write Python scripts outputting `.sc` files readable by [scadnano](https://scadnano.org), a web application useful for displaying and manually editing these structures. The purpose of this module is to help automate some of the task of creating DNA designs, as well as making large-scale changes to them that are easier to describe programmatically than to do by hand in the scadnano web interface.

Early versions of this project didn't have well-defined versions. However, we will try to announce breaking changes (and possibly new features) under the [GitHub releases page](https://github.com/UC-Davis-molecular-computing/scadnano-python-package/releases). The version numbers in this Python library repo and the [web interface repo](https://github.com/UC-Davis-molecular-computing/scadnano/releases) won't always advance at the same time.
We will try to announce breaking changes (and possibly new features) under the [GitHub releases page](https://github.com/UC-Davis-molecular-computing/scadnano-python-package/releases). The version numbers in this Python library repo and the [web interface repo](https://github.com/UC-Davis-molecular-computing/scadnano/releases) won't always advance at the same time, and sometimes a feature is supported in one before the other.

Following [semantic versioning](https://semver.org/), version numbers are major.minor.patch, i.e., version 0.9.2 has minor version number 9. Prior to version 1.0.0, when a breaking change is made, this will increment the minor version (for example, going from 0.9.4 to 0.10.0). After version 1.0.0, breaking changes will increment the major version.

Expand Down Expand Up @@ -171,7 +172,7 @@ import scadnano as sc
import modifications as mod


def create_design():
def create_design() -> sc.Design:
# helices
helices = [sc.Helix(max_offset=48), sc.Helix(max_offset=48)]

Expand Down Expand Up @@ -216,14 +217,10 @@ Running the code above produces a `.sc` file that, if loaded into scadnano, shou


## Abbreviated syntax with chained methods
Instead of explicitly creating variables and objects representing each domain in each strand, there is a shorter syntax using chained method calls. Instead of the above, create only the helices first, then create the Design. Then strands can be added using a shorter syntax, to describe how to draw the strand starting at the 5' end and moving to the 3' end. The following is a modified version of the above script using these chained methods
Instead of explicitly creating variables and objects representing each domain in each strand, there is a shorter syntax using chained method calls. Instead of the above, create only the helices first, then create the Design. Then strands can be added using a shorter syntax, to describe how to draw the strand starting at the 5' end and moving to the 3' end. The following is a modified version of the above `create_design` function using these chained methods:

```python
import scadnano as sc
import modifications as mod


def create_design():
def create_design() -> sc.Design:
# helices
helices = [sc.Helix(max_offset=48), sc.Helix(max_offset=48)]

Expand All @@ -250,16 +247,35 @@ def create_design():
design.assign_dna(design.scaffold, 'AACGT' * 18)

return design


if __name__ == '__main__':
design = create_design()
design.write_scadnano_file(directory='output_designs')
```

Documentation is available in the [API docs](https://scadnano-python-package.readthedocs.io/en/latest/#scadnano.Design.draw_strand).


## StrandBuilder object for iteratively building up strands with many domains

The method [`Design.draw_strand`](https://scadnano-python-package.readthedocs.io/en/latest/#scadnano.Design.draw_strand), as well as all those that follow it in a chained method call (e.g., `move`, `cross`, etc.) all return an instance of the class [`StrandBuilder`](https://scadnano-python-package.readthedocs.io/en/latest/#scadnano.StrandBuilder).
Above, that `StrandBuilder` instance is anonymous, i.e., never assigned to a variable.
Some long strands may be easier to specify with loops, for example an M13 scaffold strand for an origami.
If so, then to use the above methods, assign the `StrandBuilder` object to a variable, and call the relevant methods on that object to build up the strand in each iteration of the loop.
For example, the following modification of the above `create_design` function creates a linear scaffold strand that zig-zags back and forth across 32 helices:

```python
def create_design() -> sc.Design:
num_helices = 32
helices = [sc.Helix(max_offset=200) for _ in range(num_helices)]
design = sc.Design(helices=helices, grid=sc.square)
strand_builder = design.draw_strand(0, 0)
for helix in range(num_helices):
# move forward if on an even helix, otherwise move in reverse
move_distance = 200 if helix % 2 == 0 else -200
strand_builder.move(move_distance)
if helix < 31: # crossover to next helix, unless it's the last helix
strand_builder.cross(helix + 1)
strand_builder.as_scaffold()
return design
```




Expand Down
1 change: 1 addition & 0 deletions examples/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
/seed_sst.py
/16_helix_origami_barrel_from_algoSST_paper-rotator.py
/2_staple_2_helix_origami_deletions_lots_of_insertions.py
/no_git/
28 changes: 28 additions & 0 deletions examples/consecutive_domains.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import scadnano as sc

def create_design() -> sc.Design:
# shows how to make consecutive domains on a helix, separated by a crossover that appears horizontal
# this is useful when doing single-stranded tile designs, for instance, or any other design
# where we have consecutive domains on a single helix.
#
# 0 [------+^+------>
#
# 1 <------+^+------]
design = sc.Design(helices=[sc.Helix(100), sc.Helix(100)], grid=sc.square)
design.draw_strand(0, 0).move(8).move(8)
design.draw_strand(1, 16).move(-8).move(-8)

# XXX: the following code raises an exception because it tries to add a crossover where
# there already is one. This can be surprising since it would work with the following
# similar-looking design that has a single longer domain per strand
#
# 0 [------------>
#
# 1 <------------]
design.add_full_crossover(helix=0, helix2=1, offset=8, forward=True)

return design

if __name__ == '__main__':
d = create_design()
d.write_scadnano_file(directory='output_designs')
22 changes: 22 additions & 0 deletions examples/extensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import scadnano as sc


def create_design() -> sc.Design:
width = 8
helices = [sc.Helix(max_offset=32) for _ in range(3)]
design = sc.Design(helices=helices, grid=sc.square)

design.draw_strand(0, 0).extension_5p(5, display_length=2.5, display_angle=45)\
.move(width).cross(1).move(-width).loopout(2, 3).move(width)\
.extension_3p(7).with_domain_name("ext_3p")

design.draw_strand(0, 24).extension_5p(5, display_length=3.5, display_angle=60)\
.move(-width).cross(1).move(width).loopout(2, 3).move(-width)\
.extension_3p(7).with_domain_name("ext_3p_top")

return design


if __name__ == '__main__':
d = create_design()
d.write_scadnano_file(directory='output_designs')
24 changes: 24 additions & 0 deletions examples/output_designs/consecutive_domains.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"version": "0.17.3",
"grid": "square",
"helices": [
{"max_offset": 100, "grid_position": [0, 0]},
{"max_offset": 100, "grid_position": [0, 1]}
],
"strands": [
{
"color": "#f74308",
"domains": [
{"helix": 0, "forward": true, "start": 0, "end": 8},
{"helix": 0, "forward": true, "start": 8, "end": 16}
]
},
{
"color": "#57bb00",
"domains": [
{"helix": 1, "forward": false, "start": 8, "end": 16},
{"helix": 1, "forward": false, "start": 0, "end": 8}
]
}
]
}
33 changes: 33 additions & 0 deletions examples/output_designs/extensions.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"version": "0.17.3",
"grid": "square",
"helices": [
{"max_offset": 32, "grid_position": [0, 0]},
{"max_offset": 32, "grid_position": [0, 1]},
{"max_offset": 32, "grid_position": [0, 2]}
],
"strands": [
{
"color": "#f74308",
"domains": [
{"extension_num_bases": 5, "display_length": 2.5},
{"helix": 0, "forward": true, "start": 0, "end": 8},
{"helix": 1, "forward": false, "start": 0, "end": 8},
{"loopout": 3},
{"helix": 2, "forward": true, "start": 0, "end": 8},
{"extension_num_bases": 7, "name": "ext_3p"}
]
},
{
"color": "#57bb00",
"domains": [
{"extension_num_bases": 5, "display_length": 3.5, "display_angle": 60},
{"helix": 0, "forward": false, "start": 16, "end": 24},
{"helix": 1, "forward": true, "start": 16, "end": 24},
{"loopout": 3},
{"helix": 2, "forward": false, "start": 16, "end": 24},
{"extension_num_bases": 7, "name": "ext_3p_top"}
]
}
]
}
78 changes: 78 additions & 0 deletions examples/output_designs/strand_builder_loop.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"version": "0.17.3",
"grid": "square",
"helices": [
{"grid_position": [0, 0]},
{"grid_position": [0, 1]},
{"grid_position": [0, 2]},
{"grid_position": [0, 3]},
{"grid_position": [0, 4]},
{"grid_position": [0, 5]},
{"grid_position": [0, 6]},
{"grid_position": [0, 7]},
{"grid_position": [0, 8]},
{"grid_position": [0, 9]},
{"grid_position": [0, 10]},
{"grid_position": [0, 11]},
{"grid_position": [0, 12]},
{"grid_position": [0, 13]},
{"grid_position": [0, 14]},
{"grid_position": [0, 15]},
{"grid_position": [0, 16]},
{"grid_position": [0, 17]},
{"grid_position": [0, 18]},
{"grid_position": [0, 19]},
{"grid_position": [0, 20]},
{"grid_position": [0, 21]},
{"grid_position": [0, 22]},
{"grid_position": [0, 23]},
{"grid_position": [0, 24]},
{"grid_position": [0, 25]},
{"grid_position": [0, 26]},
{"grid_position": [0, 27]},
{"grid_position": [0, 28]},
{"grid_position": [0, 29]},
{"grid_position": [0, 30]},
{"grid_position": [0, 31]}
],
"strands": [
{
"color": "#0066cc",
"domains": [
{"helix": 0, "forward": true, "start": 0, "end": 200},
{"helix": 1, "forward": false, "start": 0, "end": 200},
{"helix": 2, "forward": true, "start": 0, "end": 200},
{"helix": 3, "forward": false, "start": 0, "end": 200},
{"helix": 4, "forward": true, "start": 0, "end": 200},
{"helix": 5, "forward": false, "start": 0, "end": 200},
{"helix": 6, "forward": true, "start": 0, "end": 200},
{"helix": 7, "forward": false, "start": 0, "end": 200},
{"helix": 8, "forward": true, "start": 0, "end": 200},
{"helix": 9, "forward": false, "start": 0, "end": 200},
{"helix": 10, "forward": true, "start": 0, "end": 200},
{"helix": 11, "forward": false, "start": 0, "end": 200},
{"helix": 12, "forward": true, "start": 0, "end": 200},
{"helix": 13, "forward": false, "start": 0, "end": 200},
{"helix": 14, "forward": true, "start": 0, "end": 200},
{"helix": 15, "forward": false, "start": 0, "end": 200},
{"helix": 16, "forward": true, "start": 0, "end": 200},
{"helix": 17, "forward": false, "start": 0, "end": 200},
{"helix": 18, "forward": true, "start": 0, "end": 200},
{"helix": 19, "forward": false, "start": 0, "end": 200},
{"helix": 20, "forward": true, "start": 0, "end": 200},
{"helix": 21, "forward": false, "start": 0, "end": 200},
{"helix": 22, "forward": true, "start": 0, "end": 200},
{"helix": 23, "forward": false, "start": 0, "end": 200},
{"helix": 24, "forward": true, "start": 0, "end": 200},
{"helix": 25, "forward": false, "start": 0, "end": 200},
{"helix": 26, "forward": true, "start": 0, "end": 200},
{"helix": 27, "forward": false, "start": 0, "end": 200},
{"helix": 28, "forward": true, "start": 0, "end": 200},
{"helix": 29, "forward": false, "start": 0, "end": 200},
{"helix": 30, "forward": true, "start": 0, "end": 200},
{"helix": 31, "forward": false, "start": 0, "end": 200}
],
"is_scaffold": true
}
]
}
20 changes: 20 additions & 0 deletions examples/strand_builder_loop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import scadnano as sc

def create_design() -> sc.Design:
num_helices = 32
helices = [sc.Helix(max_offset=200) for _ in range(num_helices)]
design = sc.Design(helices=helices, grid=sc.square)
strand_builder = design.draw_strand(0, 0)
for helix in range(num_helices):
# move forward if on an even helix, otherwise move in reverse
move_distance = 200 if helix % 2 == 0 else -200
strand_builder.move(move_distance)
if helix < 31: # crossover to next helix, unless it's the last helix
strand_builder.cross(helix + 1)
strand_builder.as_scaffold()
return design


if __name__ == '__main__':
design = create_design()
design.write_scadnano_file(directory='output_designs')
Loading

0 comments on commit 724a4a8

Please sign in to comment.