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

Feature/melting temp #19

Merged
merged 9 commits into from
Oct 8, 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Changed
## [0.1] - 2024-06-05
### Added
- MeltingTemperature enum
- Basic melting temperature calculation
- `addition` and `multipication` operators overload
- `len` magic overload
- `Primer` class
Expand Down
2 changes: 2 additions & 0 deletions opr/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@

PRIMER_ADDITION_ERROR = "You can only add two Primer objects."
PRIMER_MULTIPICATION_ERROR = "The primer sequence can only be multiplied by an integer."

PRIMER_SUPPORTED_MELTING_TEMPERATURE_CALCULATIONS = "This version currently supports only the Basic calculation for Melting Temperature."
54 changes: 52 additions & 2 deletions opr/primer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
"""OPR primer."""
from enum import Enum
from warnings import warn
from .errors import OPRBaseError
from .params import VALID_BASES
Expand All @@ -9,6 +10,15 @@
from .params import A_WEIGHT, T_WEIGHT, C_WEIGHT, G_WEIGHT, ANHYDROUS_MOLECULAR_WEIGHT_CONSTANT
from .params import DNA_COMPLEMENT_MAP
from .params import PRIMER_ADDITION_ERROR, PRIMER_MULTIPICATION_ERROR
from .params import PRIMER_SUPPORTED_MELTING_TEMPERATURE_CALCULATIONS


class MeltingTemperature(Enum):
"""Mode used to calculate the Melting Temperature of the Primer accordingly."""

BASIC = 1
SALT_ADJUSTED = 2
NEAREST_NEIGHBOR = 3


class Primer:
Expand All @@ -30,6 +40,11 @@ def __init__(self, primer_sequence):
self._sequence = Primer.validate_primer(primer_sequence)
self._molecular_weight = None
self._gc_content = None
self._melting_temperature = {
MeltingTemperature.BASIC: None,
MeltingTemperature.SALT_ADJUSTED: None,
MeltingTemperature.NEAREST_NEIGHBOR: None,
}

def __len__(self):
"""
Expand Down Expand Up @@ -144,8 +159,8 @@ def molecular_weight(self):
c_count = self._sequence.count('C')
g_count = self._sequence.count('G')
# Anhydrous Molecular Weight = (An x 313.21) + (Tn x 304.2) + (Cn x 289.18) + (Gn x 329.21) - 61.96
self._molecular_weight = (a_count * A_WEIGHT) + (t_count * T_WEIGHT) + (c_count * \
C_WEIGHT) + (g_count * G_WEIGHT) - ANHYDROUS_MOLECULAR_WEIGHT_CONSTANT
self._molecular_weight = (a_count * A_WEIGHT) + (t_count * T_WEIGHT) + (c_count *
C_WEIGHT) + (g_count * G_WEIGHT) - ANHYDROUS_MOLECULAR_WEIGHT_CONSTANT
return self._molecular_weight

@molecular_weight.setter
Expand Down Expand Up @@ -177,3 +192,38 @@ def gc_content(self, _):
@gc_content.deleter
def gc_content(self, _):
raise OPRBaseError(PRIMER_NOT_REMOVABLE_ATTRIBUTE_ERROR)

def melting_temperature(self, method=MeltingTemperature.BASIC):
"""
Calculate(if needed) the melting temperature.

:param method: requested calculation mode for melting temperature
:type method: MeltingTemperature
:return: approximated melting temperature
"""
if self._melting_temperature[method] != None:
return self._melting_temperature[method]
a_count = self._sequence.count('A')
t_count = self._sequence.count('T')
c_count = self._sequence.count('C')
g_count = self._sequence.count('G')
if method == MeltingTemperature.BASIC:
if len(self) <= 13:
# Tm= (wA+xT) * 2 + (yG+zC) * 4
# where w,x,y,z are the number of the bases A,T,G,C in the sequence,
# respectively (from Marmur,J., and Doty,P. (1962) J Mol Biol 5:109-118
# [PubMed]).
self._melting_temperature[MeltingTemperature.BASIC] = (
a_count + t_count) * 2 + (g_count + c_count) * 4
else:
# Tm= 64.9 +41 * (yG+zC-16.4)/(wA+xT+yG+zC)
# See Wallace,R.B., Shaffer,J., Murphy,R.F., Bonner,J., Hirose,T., and
# Itakura,K. (1979) Nucleic Acids Res 6:3543-3557 (Abstract) and
# Sambrook,J., and Russell,D.W. (2001) Molecular Cloning: A Laboratory
# Manual. Cold Spring Harbor Laboratory Press; Cold Spring Harbor, NY.
# (CHSL Press)
self._melting_temperature[MeltingTemperature.BASIC] = 64.9 + 41 * \
((g_count + c_count - 16.4) / (a_count + t_count + g_count + c_count))
else:
raise(NotImplementedError(PRIMER_SUPPORTED_MELTING_TEMPERATURE_CALCULATIONS))
return self._melting_temperature[method]
13 changes: 11 additions & 2 deletions tests/test_calculations.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
from opr import Primer
from opr.primer import MeltingTemperature

TEST_CASE_NAME = "Calculations tests"

def test_mwc():
oprimer = Primer("ATCGATCGATCGATCGAT")
assert round(oprimer.molecular_weight, 1) == 5498.7


def test_gc_content_1(): #Reference: https://jamiemcgowan.ie/bioinf/gc_content.html
oprimer = Primer("ATCG")
assert oprimer.gc_content == 0.5


def test_gc_content_2(): #Reference: https://jamiemcgowan.ie/bioinf/gc_content.html
oprimer = Primer("ATTCG")
assert oprimer.gc_content == 0.4

def test_gc_content_3(): #Reference: https://jamiemcgowan.ie/bioinf/gc_content.html
oprimer = Primer("ATTTTTT")
assert oprimer.gc_content == 0

def test_melt_temp_1(): #Reference: http://biotools.nubic.northwestern.edu/OligoCalc.html
oprimer = Primer("ATCGATCGATCGATCGATCG")
basic_melt_temp = oprimer.melting_temperature(MeltingTemperature.BASIC)
assert round(basic_melt_temp,1) == 51.8

def test_melt_temp_2(): #Reference: http://biotools.nubic.northwestern.edu/OligoCalc.html
oprimer = Primer("ATCG")
basic_melt_temp = oprimer.melting_temperature(method=MeltingTemperature.BASIC)
assert round(basic_melt_temp,1) == 12
Loading