Skip to content

Commit

Permalink
Merge branch 'dev' into add/internal_cache_flag
Browse files Browse the repository at this point in the history
  • Loading branch information
AHReccese authored Mar 1, 2025
2 parents 677ab50 + ce54d15 commit 3d5d0e5
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Salt-adjusted melting temperature calculation
### Changed
## [0.3] - 2025-02-06
### Added
- `_computed` internal cache flag attribute
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@

### Load
```pycon
>>> from opr import Primer
>>> primer1 = Primer(sequence="CTGGAGGACGGAAGAGGAAGTAA")
>>> from opr import Primer, MeltingTemperature
>>> primer1 = Primer(sequence="CTGGAGGACGGAAGAGGAAGTAA", salt=50)
>>> primer1.sequence
'CTGGAGGACGGAAGAGGAAGTAA'
```
Expand Down Expand Up @@ -107,9 +107,17 @@
1
```
#### Melting temperature
##### Basic
```pycon
>>> primer1.melting_temperature()
57.056521739130446
>>> primer1.melting_temperature(MeltingTemperature.BASIC)
57.056521739130446
```
##### Salt-adjusted
```pycon
>>> primer1.melting_temperature(MeltingTemperature.SALT_ADJUSTED)
64.64203250676053
```
### Operations

Expand Down
28 changes: 28 additions & 0 deletions opr/functions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
"""OPR functions."""
import math
from .params import A_WEIGHT, T_WEIGHT, C_WEIGHT, G_WEIGHT, ANHYDROUS_MOLECULAR_WEIGHT_CONSTANT


Expand Down Expand Up @@ -38,6 +39,33 @@ def basic_melting_temperature_calc(sequence):
return melting_temperature


def salt_adjusted_melting_temperature_calc(sequence, salt):
"""
Calculate the salt-adjusted melting temperature (Tm) of a primer sequence.
:param sequence: Primer nucleotides sequence
:type sequence: str
:param salt: Sodium ion concentration in moles (unit mM)
:type salt: float
:return: Salt-adjusted melting temperature as float
"""
a_count = sequence.count('A')
t_count = sequence.count('T')
c_count = sequence.count('C')
g_count = sequence.count('G')
seq_length = len(sequence)
if seq_length <= 13:
salt_adjustment = 16.6 * (math.log10(salt)-3) - 16.6 * math.log10(0.050)
tm = (a_count + t_count) * 2 + (g_count + c_count) * 4 + salt_adjustment
else:
tm = (
100.5 + (41 * (g_count + c_count) / seq_length)
- (820 / seq_length)
+ 16.6 * (math.log10(salt)-3)
)
return tm


def gc_clamp_calc(sequence):
"""
Calculate GC clamp.
Expand Down
10 changes: 8 additions & 2 deletions opr/primer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
from .params import DNA_COMPLEMENT_MAP
from .params import PRIMER_ADDITION_ERROR, PRIMER_MULTIPLICATION_ERROR
from .params import PRIMER_MELTING_TEMPERATURE_NOT_IMPLEMENTED_ERROR

from .params import PRIMER_ATTRIBUTE_NOT_COMPUTABLE_ERROR
from .functions import molecular_weight_calc, basic_melting_temperature_calc, gc_clamp_calc
from .functions import molecular_weight_calc, basic_melting_temperature_calc, salt_adjusted_melting_temperature_calc, gc_clamp_calc


class MeltingTemperature(Enum):
Expand All @@ -31,14 +32,16 @@ class Primer:
>>> oprimer.molecular_weight
"""

def __init__(self, sequence, name=DEFAULT_PRIMER_NAME):
def __init__(self, sequence, name=DEFAULT_PRIMER_NAME, salt=50):
"""
Initialize the Primer instance.
:param sequence: primer nucleotides sequence
:type sequence: str
:param name: primer name
:type name: str
:param salt: Sodium ion concentration in moles (unit mM)
:type salt: float
:return: an instance of the Primer class
"""
self._sequence = Primer.validate_primer(sequence)
Expand All @@ -48,6 +51,7 @@ def __init__(self, sequence, name=DEFAULT_PRIMER_NAME):
self._gc_clamp = None
self._single_runs = None
self._double_runs = None
self._salt_level = salt
self._melting_temperature = {
MeltingTemperature.BASIC: None,
MeltingTemperature.SALT_ADJUSTED: None,
Expand Down Expand Up @@ -311,6 +315,8 @@ def melting_temperature(self, method=MeltingTemperature.BASIC):
return self._melting_temperature[method]
if method == MeltingTemperature.BASIC:
self._melting_temperature[MeltingTemperature.BASIC] = basic_melting_temperature_calc(self._sequence)
elif method == MeltingTemperature.SALT_ADJUSTED:
self._melting_temperature[MeltingTemperature.SALT_ADJUSTED] = salt_adjusted_melting_temperature_calc(self._sequence, self._salt_level)
else:
raise NotImplementedError(PRIMER_MELTING_TEMPERATURE_NOT_IMPLEMENTED_ERROR)
self._computed["melting_temperature"][method] = True
Expand Down
24 changes: 24 additions & 0 deletions tests/test_calculations.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,30 @@ def test_melt_temp_2(): # Reference: http://biotools.nubic.northwestern.edu/Oli
assert round(basic_melt_temp, 1) == 12


def test_melt_temp_3(): # Reference: http://biotools.nubic.northwestern.edu/OligoCalc.html
oprimer = Primer("CTGGAGGACGGAAGAGGAAGTAA")
salt_adjusted_melt_temp = oprimer.melting_temperature(method=MeltingTemperature.SALT_ADJUSTED)
assert round(salt_adjusted_melt_temp, 0) == 65.0


def test_melt_temp_4(): # Reference: http://biotools.nubic.northwestern.edu/OligoCalc.html
oprimer = Primer("CTGGAGGACGGAAGAGGAAGTAAA", salt=65)
salt_adjusted_melt_temp = oprimer.melting_temperature(method=MeltingTemperature.SALT_ADJUSTED)
assert round(salt_adjusted_melt_temp, 0) == 67.0


def test_melt_temp_5(): # Reference: http://biotools.nubic.northwestern.edu/OligoCalc.html
oprimer = Primer("CTGGAGG")
salt_adjusted_melt_temp = oprimer.melting_temperature(method=MeltingTemperature.SALT_ADJUSTED)
assert round(salt_adjusted_melt_temp, 0) == 24.0


def test_melt_temp_6(): # Reference: http://biotools.nubic.northwestern.edu/OligoCalc.html
oprimer = Primer("CTGGAGG", salt=65)
salt_adjusted_melt_temp = oprimer.melting_temperature(method=MeltingTemperature.SALT_ADJUSTED)
assert round(salt_adjusted_melt_temp, 0) == 26.0


def test_single_runs_1(): # Reference: https://www.oligoevaluator.com/OligoCalcServlet
oprimer = Primer("ATCGATCG")
runs = oprimer.single_runs
Expand Down

0 comments on commit 3d5d0e5

Please sign in to comment.