Skip to content

Commit

Permalink
Merge pull request #307 from UC-Davis-molecular-computing/306-allow-p…
Browse files Browse the repository at this point in the history
…er-helix-group-geometry

306 allow per helix group geometry
  • Loading branch information
dave-doty authored Sep 21, 2024
2 parents e8905fa + d6a52bd commit eb458c3
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 15 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/run_unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ 3.7, 3.8, 3.9, "3.10", "3.11" ]
python-version: [ 3.7, 3.8, 3.9, "3.10", "3.11", "3.12" ]

steps:
- uses: actions/checkout@v2
Expand All @@ -21,8 +21,8 @@ jobs:
with:
activate-conda: true
- name: Install openpyxl,tabulate with conda
run: conda install openpyxl=3.0.10 tabulate=0.8.10
run: conda install openpyxl=3.1 tabulate=0.9
- name: Install docutils with conda
run: conda install docutils=0.16
run: conda install docutils=0.18
- name: Test with unittest
run: python -m unittest -v tests/scadnano_tests.py
1 change: 0 additions & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ channels:
- defaults
dependencies:
- python=3.7
- xlwt=1.3.0=py37_0
- pip
- pip:
- sphinx
Expand Down
2 changes: 0 additions & 2 deletions examples/1_staple_1_helix_origami_roll.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import math

import scadnano as sc

def create_design() -> sc.Design:
Expand Down
24 changes: 24 additions & 0 deletions examples/2_staple_2_helix_helixgroup_geometry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import scadnano as sc


def create_design() -> sc.Design:
group0 = sc.HelixGroup(grid=sc.square)
group1 = sc.HelixGroup(grid=sc.square, geometry=sc.Geometry(bases_per_turn=18),
position=sc.Position3D(0, 3, 0))
groups = {"group 0": group0, "group 1": group1}
helices = [sc.Helix(idx=idx, max_offset=40, group=group) for idx, group in
[(0, "group 0"), (1, "group 1")]]
design = sc.Design(helices=helices, groups=groups, strands=[])
design.draw_strand(0, 0).move(40)
design.draw_strand(0, 40).move(-40)
design.draw_strand(1, 0).move(40)
design.draw_strand(1, 40).move(-40)

return design


if __name__ == '__main__':
d = create_design()
d.write_scadnano_file(directory='output_designs')
d.from_scadnano_file('output_designs/2_staple_2_helix_helixgroup_geometry.sc')
print(f'design: {d.to_json()}')
46 changes: 46 additions & 0 deletions examples/output_designs/2_staple_2_helix_helixgroup_geometry.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"version": "0.19.4",
"groups": {
"group 0": {
"position": {"x": 0, "y": 0, "z": 0},
"grid": "square"
},
"group 1": {
"position": {"x": 0, "y": 3, "z": 0},
"grid": "square",
"geometry": {
"bases_per_turn": 18
}
}
},
"helices": [
{"group": "group 0", "grid_position": [0, 0]},
{"group": "group 1", "grid_position": [0, 0]}
],
"strands": [
{
"color": "#f74308",
"domains": [
{"helix": 0, "forward": true, "start": 0, "end": 40}
]
},
{
"color": "#57bb00",
"domains": [
{"helix": 0, "forward": false, "start": 0, "end": 40}
]
},
{
"color": "#888888",
"domains": [
{"helix": 1, "forward": true, "start": 0, "end": 40}
]
},
{
"color": "#32b86c",
"domains": [
{"helix": 1, "forward": false, "start": 0, "end": 40}
]
}
]
}
32 changes: 25 additions & 7 deletions scadnano/scadnano.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
# needed to use forward annotations: https://docs.python.org/3/whatsnew/3.7.html#whatsnew37-pep563
from __future__ import annotations

__version__ = "0.19.4" # version line; WARNING: do not remove or change this line or comment
__version__ = "0.19.5" # version line; WARNING: do not remove or change this line or comment

import collections
import dataclasses
Expand Down Expand Up @@ -1315,6 +1315,14 @@ class HelixGroup(_JSONSerializable):
grid: Grid = Grid.none
""":any:`Grid` of this :any:`HelixGroup` used to interpret the field :data:`Helix.grid_position`."""

geometry: Optional[Geometry] = None
"""
Optional custom :any:`Geometry` to specify for this :any:`HelixGroup`. If specified then
it is assumed to override the field :data:`Design.geometry`. This will affect, for instance,
where nucleotides and phosphate groups are placed when exporting to oxDNA or oxView via
:meth:`Design.to_oxview_format` or :meth:`Design.to_oxdna_format`.
"""

def has_default_position_and_orientation(self):
# we don't bother checking grid or helices_view_order because those are written to top-level
# fields of Design if the group is otherwise default
Expand All @@ -1341,6 +1349,9 @@ def to_json_serializable(self, suppress_indent: bool = True, **kwargs: Any) -> D
if self.helices_view_order != default_helices_view_order:
dct[helices_view_order_key] = NoIndent(self.helices_view_order)

if self.geometry is not None:
dct[geometry_key] = self.geometry.to_json_serializable(suppress_indent)

return dct

def _assign_default_helices_view_order(self, helices_in_group: Dict[int, 'Helix']) -> None:
Expand Down Expand Up @@ -1372,12 +1383,19 @@ def from_json(json_map: dict, **kwargs: Any) -> HelixGroup:
roll = json_map.get(roll_key, default_roll)
yaw = json_map.get(yaw_key, default_yaw)

return HelixGroup(position=position,
pitch=pitch,
yaw=yaw,
roll=roll,
helices_view_order=helices_view_order,
grid=grid)
geometry = None
if geometry_key in json_map:
geometry = Geometry.from_json(json_map[geometry_key])

return HelixGroup(
position=position,
pitch=pitch,
yaw=yaw,
roll=roll,
helices_view_order=helices_view_order,
grid=grid,
geometry=geometry,
)

def helices_view_order_inverse(self, idx: int) -> int:
"""
Expand Down
7 changes: 5 additions & 2 deletions tests/scadnano_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1282,7 +1282,7 @@ def test_write_idt_plate_excel_file(self) -> None:
# add 10 strands in excess of 3 plates
for plate_type in [sc.PlateType.wells96, sc.PlateType.wells384]:
num_strands = 3 * plate_type.num_wells_per_plate() + 10
filename = f'test_excel_export_{plate_type.num_wells_per_plate()}.xlsx'
filename = f'tests/test_excel_export_{plate_type.num_wells_per_plate()}.xlsx'
max_offset = num_strands * strand_len
helices = [sc.Helix(max_offset=max_offset) for _ in range(1)]
design = sc.Design(helices=helices, strands=[], grid=sc.square)
Expand All @@ -1307,7 +1307,10 @@ def test_write_idt_plate_excel_file(self) -> None:

self.assertEqual(expected_wells + 1, sheet.max_row)

os.remove(filename)
try:
os.remove(filename)
except PermissionError as e:
print(f'could not remove file "{filename}" due to permission error')

def test_export_dna_sequences_extension_5p(self) -> None:
design = sc.Design(helices=[sc.Helix(max_offset=100)])
Expand Down
Binary file added tests/test_excel_export_384.xlsx
Binary file not shown.
Binary file removed tests/test_excel_export_96.xls
Binary file not shown.
Binary file added tests/test_excel_export_96.xlsx
Binary file not shown.

0 comments on commit eb458c3

Please sign in to comment.