Skip to content

Commit

Permalink
feat: add thermo attributes to Oligo and Primer3 parsing logic; docs:…
Browse files Browse the repository at this point in the history
… update doctest examples
  • Loading branch information
emmcauley committed Oct 3, 2024
1 parent 7ae349c commit 2393014
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 7 deletions.
33 changes: 29 additions & 4 deletions prymer/api/oligo.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,27 @@
class Oligo(OligoLike, Metric["Oligo"]):
"""Stores the properties of the designed oligo.
Oligos can include both single primer and internal probe designs. The penalty score of the
design is emitted by Primer3 and controlled by the corresponding design parameters.
The penalty for a primer is set by the combination of `PrimerAndAmpliconParameters` and
`PrimerWeights`, whereas a probe penalty is set by `ProbeParameters` and `ProbeWeights`.
Oligos can include both single primer and internal probe designs. Primer3 emits information
about the specific properties of a primer and/or probe, including the base sequence,
melting temperature (tm), and score.
The penalty score of the design is emitted by Primer3 and controlled by the corresponding
design parameters. The penalty for a primer is set by the combination of
`PrimerAndAmpliconParameters` and `PrimerWeights`.
A probe penalty is set by `ProbeParameters` and `ProbeWeights`.
The span of an `Oligo` object represents the mapping of the oligo
to the genome. `Oligo` objects can optionally have a `tail` sequence to prepend to the 5' end
of the primer or probe, as well as a `name` for downstream record keeping.
`Oligo` objects can also optionally store thermodynamic characteristics of primer and/or probe
design. These include the calculated melting temperatures of the most stable hairpin structure,
self-, and end- complementarity. These values can be emitted by Primer3.
These thermodynamic characteristics are meant to quantify how likely it is the primer or probe
will bind to itself (e.g., instead of the target DNA). A melting temperature close to or greater
than the intended melting temperature for target binding indicates the primer or probe is
likely to form stable hairpins or dimers, leading to reduced efficiency of the reaction.
Attributes:
tm: the calculated melting temperature of the oligo
Expand All @@ -95,6 +112,11 @@ class Oligo(OligoLike, Metric["Oligo"]):
bases: the base sequence of the oligo (excluding any tail)
tail: an optional tail sequence to put on the 5' end of the primer
name: an optional name to use for the primer
self_any_th: an optional calculated melting temperature that represents
the tendency of an oligo to bind to itself (self-complementarity)
self_end_th: an optional calculated melting temperature that represents
the tendency of a primer to bind to the 3'-END of an identical primer
hairpin_th: an optional calculated melting temperature of the oligo hairpin structure
"""

Expand All @@ -103,6 +125,9 @@ class Oligo(OligoLike, Metric["Oligo"]):
span: Span
bases: Optional[str] = None
tail: Optional[str] = None
self_any_th: Optional[float] = None
self_end_th: Optional[float] = None
hairpin_th: Optional[float] = None

def __post_init__(self) -> None:
super(Oligo, self).__post_init__()
Expand Down
4 changes: 2 additions & 2 deletions prymer/api/primer_pair.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ class methods to represent a primer pair. The primer pair is comprised of a lef
Span(refname='chr1', start=21, end=100, strand=<Strand.POSITIVE: '+'>)
>>> list(primer_pair)
[Oligo(name=None, tm=70.0, penalty=-123.0, span=Span(refname='chr1', start=1, end=20, strand=<Strand.POSITIVE: '+'>), bases='GGGGGGGGGGGGGGGGGGGG', tail=None), Oligo(name=None, tm=70.0, penalty=-123.0, span=Span(refname='chr1', start=101, end=120, strand=<Strand.NEGATIVE: '-'>), bases='TTTTTTTTTTTTTTTTTTTT', tail=None)]
[Oligo(name=None, tm=70.0, penalty=-123.0, span=Span(refname='chr1', start=1, end=20, strand=<Strand.POSITIVE: '+'>), bases='GGGGGGGGGGGGGGGGGGGG', tail=None, self_any_th=None, self_end_th=None, hairpin_th=None), Oligo(name=None, tm=70.0, penalty=-123.0, span=Span(refname='chr1', start=101, end=120, strand=<Strand.NEGATIVE: '-'>), bases='TTTTTTTTTTTTTTTTTTTT', tail=None, self_any_th=None, self_end_th=None, hairpin_th=None)]
```
""" # noqa: E501

from dataclasses import dataclass
Expand Down
16 changes: 15 additions & 1 deletion prymer/primer3/primer3.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,13 +530,27 @@ def _build_oligos(
bases = unmasked_design_seq[slice_offset:slice_end]
if span.strand == Strand.NEGATIVE:
bases = reverse_complement(bases)

# assemble Primer3-emitted results into `Oligo` objects
# if thermodynamic melting temperatures are missing, default to None
primers.append(
Oligo(
bases=bases,
tm=float(design_results[f"PRIMER_{design_task.task_type}_{idx}_TM"]),
penalty=float(design_results[f"PRIMER_{design_task.task_type}_{idx}_PENALTY"]),
span=span,
self_any_th=float(
design_results.get(
f"PRIMER_{design_task.task_type}_{idx}_SELF_ANY_TH", None
)
),
self_end_th=float(
design_results.get(
f"PRIMER_{design_task.task_type}_{idx}_SELF_END_TH", None
)
),
hairpin_th=float(
design_results.get(f"PRIMER_{design_task.task_type}_{idx}_HAIRPIN_TH", None)
),
)
)
return primers
Expand Down

0 comments on commit 2393014

Please sign in to comment.