Pair strands¶
Add hydrogen bond pairings between two RNA strands to build a duplex (siRNA).
Workflow:
- Load a molecule with two single strands
- Add hydrogen bond pairings using
modify("complete_h_pairs", ...) - Visualize the duplex
The system automatically validates base pairings and warns about mismatches.
Setup¶
HELMshaker bundles no monomers, so every example needs a monomer library.
These docs use a small public HELMCore subset committed at
docs/examples/docs_library.json. In your own code, pull a dictionary from
TMR instead. See Load a monomer library.
In [1]:
Copied!
from helmshaker import Molecule, MonomerLibrary
library = MonomerLibrary()
library.load_from_file("../examples/docs_library.json")
print(f"{len(library)} monomers available")
from helmshaker import Molecule, MonomerLibrary
library = MonomerLibrary()
library.load_from_file("../examples/docs_library.json")
print(f"{len(library)} monomers available")
40 monomers available
Step 1: Load Two Single Strands¶
In [2]:
Copied!
from helmshaker import Molecule
xna = {
"polymers": {
"RNA1": {
"base": "A.A.A.G.A.G.U.A.A.A.A.A.A.U.C.G.A.G.A.U.U",
"phosphate": "sp.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.sp.p.",
"sugar": "m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.m.m"
},
"RNA2": {
"base": "U.C.U.C.G.U.U.U.U.U.U.U.A.A.U.U.U.U.U",
"phosphate": "sp.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.sp",
"sugar": "fl2r.fl2r.fl2r.fl2r.fl2r.fl2r.fl2r.m.fl2r.m.fl2r.m.fl2r.fl2r.fl2r.fl2r.fl2r.fl2r.fl2r"
}
}
}
molecule = Molecule.from_xna(xna, monomer_library=library)
molecule.display()
from helmshaker import Molecule
xna = {
"polymers": {
"RNA1": {
"base": "A.A.A.G.A.G.U.A.A.A.A.A.A.U.C.G.A.G.A.U.U",
"phosphate": "sp.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.sp.p.",
"sugar": "m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.m.m"
},
"RNA2": {
"base": "U.C.U.C.G.U.U.U.U.U.U.U.A.A.U.U.U.U.U",
"phosphate": "sp.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.sp",
"sugar": "fl2r.fl2r.fl2r.fl2r.fl2r.fl2r.fl2r.m.fl2r.m.fl2r.m.fl2r.fl2r.fl2r.fl2r.fl2r.fl2r.fl2r"
}
}
}
molecule = Molecule.from_xna(xna, monomer_library=library)
molecule.display()
Display MoleculeData: polymers=2 (RNA=2, CHEM=0), connections=0
──────────────────────────────────────────────────── Structure ────────────────────────────────────────────────────
RNA1 RNA2
Connectivity ┏━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━┓ ┃ Pair ┃ H-bonds ┃ Covalent ┃ ┡━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━┩ │ (unconnected) │ │ RNA1, RNA2 │ └───────────────┴─────────┴────────────┘
Summary ┏━━━━━━━━━┳━━━━━━┳━━━━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━┓ ┃ Polymer ┃ Type ┃ Residues ┃ Sugars ┃ Bases ┃ Phosphates ┃ ┡━━━━━━━━━╇━━━━━━╇━━━━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━┩ │ RNA1 │ RNA │ 21 │ 21 │ 21 │ 20 │ │ RNA2 │ RNA │ 19 │ 19 │ 19 │ 19 │ └─────────┴──────┴──────────┴────────┴───────┴────────────┘
CHEM Data ┏━━━━━━┳━━━━━━━━━━━┓ ┃ CHEM ┃ Chemistry ┃ ┡━━━━━━╇━━━━━━━━━━━┩ └──────┴───────────┘
RNA Data 5' -> 3' ┏━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ RNA ┃ Sugar ┃ Base ┃ Phosphate ┃ ┡━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ │ RNA1 │ m.fl2r.m.fl2r.m.fl2r.m.fl2r.m.fl… │ A.A.A.G.A.G.U.A.A.A.A.A.A.U.C.G.… │ sp.p.p.p.p.p.p.p.p.p.p.p.p.p.p.… │ │ RNA2 │ fl2r.fl2r.fl2r.fl2r.fl2r.fl2r.fl… │ U.C.U.C.G.U.U.U.U.U.U.U.A.A.U.U.… │ sp.p.p.p.p.p.p.p.p.p.p.p.p.p.p.… │ └──────┴───────────────────────────────────┴───────────────────────────────────┴──────────────────────────────────┘
Connections ┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ Source PolymerID ┃ Target PolymerID ┃ SourceMonomerPosition:SourceAttach… ┃ TargetMonomerPosition:TargetAttach… ┃ ┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ └──────────────────┴──────────────────┴─────────────────────────────────────┴─────────────────────────────────────┘
Step 2: Check Current Connections¶
Before adding pairings, the molecule has no connections.
In [3]:
Copied!
# No connections yet
print(f"Connections: {molecule.data.connections}")
print(f"HELM: {molecule.to_helm()}")
# No connections yet
print(f"Connections: {molecule.data.connections}")
print(f"HELM: {molecule.to_helm()}")
Connections: []
HELM: RNA1{m(A)[sp].[fl2r](A)p.m(A)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(U)p.[fl2r](A)p.m(A)p.[fl2r](A)p.m(A)p.[fl2r](A)p.m(A)p.[fl2r](U)p.m(C)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(A)[sp].m(U)p.m(U)}|RNA2{[fl2r](U)[sp].[fl2r](C)p.[fl2r](U)p.[fl2r](C)p.[fl2r](G)p.[fl2r](U)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(U)p.[fl2r](A)p.[fl2r](A)p.[fl2r](U)p.[fl2r](U)p.[fl2r](U)p.[fl2r](U)p.[fl2r](U)[sp]}$$$$V2.0
Step 3: Add Hydrogen Bond Pairings¶
Use modify("complete_h_pairs", ...) to add base pairings:
rna_id_sense: ID of the sense strandrna_id_antisense: ID of the antisense strandbase_1_sense,base_2_sense: Range of bases on sense strandbase_1_antisense,base_2_antisense: Corresponding bases on antisense strandautofill=True: Automatically fill in pairings between the endpoints
In [4]:
Copied!
molecule.modify(
"complete_h_pairs",
rna_id_sense="RNA1",
rna_id_antisense="RNA2",
base_1_sense=1,
base_1_antisense=19,
base_2_sense=19,
base_2_antisense=1,
autofill=True
)
molecule.modify(
"complete_h_pairs",
rna_id_sense="RNA1",
rna_id_antisense="RNA2",
base_1_sense=1,
base_1_antisense=19,
base_2_sense=19,
base_2_antisense=1,
autofill=True
)
Out[4]:
<helmshaker.molecule.Molecule at 0x7f123c72c520>
Step 4: Check for Mismatches¶
If there are mismatched base pairs, you'll see warnings. You can also check the validation result.
In [5]:
Copied!
molecule.validate()
result = molecule.get_validation_result()
print(f"Is valid: {result.is_valid}")
print(f"Warnings: {result.warnings}")
print(f"Mismatched pairs: {result.mismatch_base_pairs}")
molecule.validate()
result = molecule.get_validation_result()
print(f"Is valid: {result.is_valid}")
print(f"Warnings: {result.warnings}")
print(f"Mismatched pairs: {result.mismatch_base_pairs}")
Is valid: True Warnings: [] Mismatched pairs: []
Step 5: View the Connections¶
In [6]:
Copied!
# Now we have hydrogen bond connections
print(f"Number of connections: {len(molecule.data.connections)}")
molecule.data.connections[:5] # Show first 5
# Now we have hydrogen bond connections
print(f"Number of connections: {len(molecule.data.connections)}")
molecule.data.connections[:5] # Show first 5
Number of connections: 16
Out[6]:
[Connection(monomer1=Monomer(id='A', type='base', uuid=''), attachment1='pair', monomer2=Monomer(id='U', type='base', uuid=''), attachment2='pair'), Connection(monomer1=Monomer(id='A', type='base', uuid=''), attachment1='pair', monomer2=Monomer(id='U', type='base', uuid=''), attachment2='pair'), Connection(monomer1=Monomer(id='A', type='base', uuid=''), attachment1='pair', monomer2=Monomer(id='U', type='base', uuid=''), attachment2='pair'), Connection(monomer1=Monomer(id='A', type='base', uuid=''), attachment1='pair', monomer2=Monomer(id='U', type='base', uuid=''), attachment2='pair'), Connection(monomer1=Monomer(id='U', type='base', uuid=''), attachment1='pair', monomer2=Monomer(id='A', type='base', uuid=''), attachment2='pair')]
Step 6: Visualize the Duplex¶
In [7]:
Copied!
fig, ax = molecule.visualize_cartoon(title="siRNA Duplex", show_base=True)
fig, ax = molecule.visualize_cartoon(title="siRNA Duplex", show_base=True)
Step 7: Export the Result¶
In [8]:
Copied!
# The HELM now includes the hydrogen bond pairings
molecule.to_helm()
# The HELM now includes the hydrogen bond pairings
molecule.to_helm()
Out[8]:
'RNA1{m(A)[sp].[fl2r](A)p.m(A)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(U)p.[fl2r](A)p.m(A)p.[fl2r](A)p.m(A)p.[fl2r](A)p.m(A)p.[fl2r](U)p.m(C)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(A)[sp].m(U)p.m(U)}|RNA2{[fl2r](U)[sp].[fl2r](C)p.[fl2r](U)p.[fl2r](C)p.[fl2r](G)p.[fl2r](U)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(U)p.[fl2r](A)p.[fl2r](A)p.[fl2r](U)p.[fl2r](U)p.[fl2r](U)p.[fl2r](U)p.[fl2r](U)[sp]}$RNA1,RNA2,2:pair-56:pair|RNA1,RNA2,5:pair-53:pair|RNA1,RNA2,8:pair-50:pair|RNA1,RNA2,14:pair-44:pair|RNA1,RNA2,20:pair-38:pair|RNA1,RNA2,23:pair-35:pair|RNA1,RNA2,26:pair-32:pair|RNA1,RNA2,29:pair-29:pair|RNA1,RNA2,32:pair-26:pair|RNA1,RNA2,35:pair-23:pair|RNA1,RNA2,38:pair-20:pair|RNA1,RNA2,44:pair-14:pair|RNA1,RNA2,47:pair-11:pair|RNA1,RNA2,50:pair-8:pair|RNA1,RNA2,53:pair-5:pair|RNA1,RNA2,56:pair-2:pair$$$V2.0'
Alternative: Use align_and_pair_strands()¶
For simpler cases, you can use align_and_pair_strands() which automatically aligns and pairs two strands.
In [9]:
Copied!
from helmshaker import Molecule
# Create from sequences
molecule = Molecule.from_sequence(["AUGCUGACUUGCUAUGCUAA", "UUAGCAUAGCAAGUCAGCAU"], monomer_library=library)
# Automatically align and pair
molecule.align_and_pair_strands()
fig, ax = molecule.visualize_cartoon(title="Auto-aligned Duplex", show_base=True)
from helmshaker import Molecule
# Create from sequences
molecule = Molecule.from_sequence(["AUGCUGACUUGCUAUGCUAA", "UUAGCAUAGCAAGUCAGCAU"], monomer_library=library)
# Automatically align and pair
molecule.align_and_pair_strands()
fig, ax = molecule.visualize_cartoon(title="Auto-aligned Duplex", show_base=True)