Your first oligonucleotide¶
By the end of this page you will have read a modified oligonucleotide from HELM, looked at what HELMshaker made of it, converted it to two other notations, changed a monomer, and drawn it.
It takes about ten minutes. Every cell runs in order, so work down the page.
You need the viz extra for the last step:
pip install "helmshaker[viz]"
Step 1: give HELMshaker a monomer library¶
A HELM string records which symbols appear in which order. It does not record what those symbols mean, so HELMshaker needs a dictionary that maps each symbol to a structure. It ships none, and refuses to guess.
These tutorials use a small public dictionary committed alongside the docs. In your own work you would pull one from TMR instead.
from helmshaker import Molecule, MonomerLibrary
library = MonomerLibrary()
library.load_from_file("../examples/docs_library.json")
print(f"{len(library)} monomers available")
40 monomers available
If you skip this step, every read raises MonomerLibraryError. That is
deliberate: reading against the wrong dictionary produces a plausible molecule
made of the wrong things.
Step 2: read a molecule¶
This is a short gapmer: three 2'-MOE residues, three deoxyribose residues, three more MOE, joined by phosphorothioate linkers.
helm = (
"RNA1{[moe](A)[sp].[moe](C)[sp].[moe](G)[sp]."
"[d](T)[sp].[d](A)[sp].[d](C)[sp]."
"[moe](G)[sp].[moe](U)[sp].[moe](A)}$$$$V2.0"
)
oligo = Molecule.from_helm(helm, monomer_library=library)
oligo
<helmshaker.molecule.Molecule at 0x7f72ccbfaf50>
Step 3: look at what you got¶
Molecule.data returns the parsed structure. It is a deep copy, so poking at it
cannot corrupt the molecule.
data = oligo.data
print("polymers :", list(data.polymers))
print("residues :", len(data.polymers["RNA1"].residues))
print("sugars :", data.get_count_monomers_by_type("RNA1", "sugar"))
print("phosphates :", data.get_count_monomers_by_type("RNA1", "phosphate"))
polymers : ['RNA1'] residues : 9 sugars : 9 phosphates : 8
Each residue holds its monomers separately, so you can see how a residue is built:
first = oligo.data.polymers["RNA1"].residues[0]
for monomer in first.monomers:
print(f" {monomer.type:<10} {monomer.id}")
sugar moe base A phosphate sp
Step 4: convert it¶
The to_X methods write the same molecule in another notation. FASTA collapses
each residue to its natural analog, which is why the modifications disappear:
print(oligo.to_fasta())
ACGTACGUA
XNA keeps them, by splitting the molecule into three parallel columns:
oligo.to_xna()
defaultdict(dict,
{'polymers': defaultdict(dict,
{'RNA1': {'sugar': 'moe.moe.moe.d.d.d.moe.moe.moe',
'base': 'A.C.G.T.A.C.G.U.A',
'phosphate': 'sp.sp.sp.sp.sp.sp.sp.sp.'}}),
'connections': []})
Read the columns down, not across: position 1 is sugar moe, base A,
phosphate sp. The trailing dot in the phosphate column marks the 3' end, which
has no linker after it.
Step 5: change a monomer¶
Modification methods change the molecule in place and return it, so you can chain them. Here the sugar at position 5 becomes an unlocked ribose:
oligo.modify("replace_single", polymer_id="RNA1", pos=5, type="sugar", new_monomer="una")
print(oligo.to_helm())
RNA1{[moe](A)[sp].[moe](C)[sp].[moe](G)[sp].d(T)[sp].[una](A)[sp].d(C)[sp].[moe](G)[sp].[moe](U)[sp].[moe](A)}$$$$V2.0
For a whole pattern rather than one position, use a named pattern. HELMshaker
ships several; full_phosphorothioate converts every linker at once.
native = Molecule.from_sequence("ACGUACGUACGU", monomer_library=library)
native.apply_pattern("full_phosphorothioate")
print(native.to_helm())
RNA1{r(A)[sp].r(C)[sp].r(G)[sp].r(U)[sp].r(A)[sp].r(C)[sp].r(G)[sp].r(U)[sp].r(A)[sp].r(C)[sp].r(G)[sp].r(U)}$$$$V2.0
Step 6: check it¶
validate() reports whether every monomer resolved and every connection makes
sense. It returns the molecule, so it chains too.
result = native.validate().get_validation_result()
print("valid :", result.is_valid)
print("errors :", result.errors)
print("warnings:", result.warnings)
valid : True errors : [] warnings: []
Step 7: draw it¶
visualize_cartoon returns a matplotlib figure and axes, so you can keep
customizing before saving.
fig, ax = native.visualize_cartoon(
title="Fully phosphorothioated 12-mer",
show_position=True,
show_base=True,
show_direction=True,
)
There is also a console rendering, which is useful over SSH or in a log:
native.display()
Display MoleculeData: polymers=1 (RNA=1, CHEM=0), connections=0
──────────────────────────────────────────────────── Structure ────────────────────────────────────────────────────
RNA1
Connectivity ┏━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┓ ┃ Pair ┃ H-bonds ┃ Covalent ┃ ┡━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━┩ │ (unconnected) │ │ RNA1 │ └───────────────┴─────────┴──────────┘
Summary ┏━━━━━━━━━┳━━━━━━┳━━━━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━┓ ┃ Polymer ┃ Type ┃ Residues ┃ Sugars ┃ Bases ┃ Phosphates ┃ ┡━━━━━━━━━╇━━━━━━╇━━━━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━┩ │ RNA1 │ RNA │ 12 │ 12 │ 12 │ 11 │ └─────────┴──────┴──────────┴────────┴───────┴────────────┘
CHEM Data ┏━━━━━━┳━━━━━━━━━━━┓ ┃ CHEM ┃ Chemistry ┃ ┡━━━━━━╇━━━━━━━━━━━┩ └──────┴───────────┘
RNA Data 5' -> 3' ┏━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ RNA ┃ Sugar ┃ Base ┃ Phosphate ┃ ┡━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ │ RNA1 │ r.r.r.r.r.r.r.r.r.r.r.r │ A.C.G.U.A.C.G.U.A.C.G.U │ sp.sp.sp.sp.sp.sp.sp.sp.sp.sp.sp. │ └──────┴─────────────────────────┴─────────────────────────┴───────────────────────────────────┘
Connections ┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ Source PolymerID ┃ Target PolymerID ┃ SourceMonomerPosition:SourceAttach… ┃ TargetMonomerPosition:TargetAttach… ┃ ┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ └──────────────────┴──────────────────┴─────────────────────────────────────┴─────────────────────────────────────┘
What next¶
- Your first peptide does the same tour for peptides
- End to end builds a conjugated siRNA duplex from scratch
- Load a monomer library covers pulling a real dictionary from TMR
- Why a monomer library is mandatory explains step 1 properly