diff --git a/CHANGELOG.md b/CHANGELOG.md index f4d02cb..84ba67d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/opr/params.py b/opr/params.py index b065ea4..6d0dcd6 100644 --- a/opr/params.py +++ b/opr/params.py @@ -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." diff --git a/opr/primer.py b/opr/primer.py index f6fc42a..3f7898b 100644 --- a/opr/primer.py +++ b/opr/primer.py @@ -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 @@ -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: @@ -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): """ @@ -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 @@ -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] diff --git a/tests/test_calculations.py b/tests/test_calculations.py index e21e729..d8a2192 100644 --- a/tests/test_calculations.py +++ b/tests/test_calculations.py @@ -1,4 +1,5 @@ from opr import Primer +from opr.primer import MeltingTemperature TEST_CASE_NAME = "Calculations tests" @@ -6,12 +7,10 @@ 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 @@ -19,3 +18,13 @@ def test_gc_content_2(): #Reference: https://jamiemcgowan.ie/bioinf/gc_content.h 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