Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

306 allow per helix group geometry #307

Merged
merged 3 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Loading