@teselagen/sequence-utils 0.3.37 → 0.3.38-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DNAComplementMap.d.ts +1 -1
- package/addGapsToSeqReads.d.ts +16 -3
- package/adjustAnnotationsToInsert.d.ts +2 -1
- package/adjustBpsToReplaceOrInsert.d.ts +2 -1
- package/aliasedEnzymesByName.d.ts +37 -1
- package/aminoAcidToDegenerateDnaMap.d.ts +1 -31
- package/aminoAcidToDegenerateRnaMap.d.ts +1 -1
- package/annotateSingleSeq.d.ts +5 -4
- package/annotationTypes.d.ts +2 -2
- package/autoAnnotate.d.ts +17 -8
- package/bioData.d.ts +10 -58
- package/calculateEndStability.d.ts +1 -1
- package/calculateNebTa.d.ts +6 -1
- package/calculateNebTm.d.ts +6 -4
- package/calculatePercentGC.d.ts +1 -1
- package/calculateSantaLuciaTm.d.ts +28 -114
- package/calculateTm.d.ts +13 -1
- package/computeDigestFragments.d.ts +30 -24
- package/condensePairwiseAlignmentDifferences.d.ts +1 -1
- package/convertAACaretPositionOrRangeToDna.d.ts +2 -1
- package/convertDnaCaretPositionOrRangeToAA.d.ts +2 -1
- package/cutSequenceByRestrictionEnzyme.d.ts +2 -1
- package/defaultEnzymesByName.d.ts +2 -1
- package/degenerateDnaToAminoAcidMap.d.ts +1 -1
- package/degenerateRnaToAminoAcidMap.d.ts +1 -1
- package/deleteSequenceDataAtRange.d.ts +2 -1
- package/diffUtils.d.ts +9 -7
- package/doesEnzymeChopOutsideOfRecognitionSite.d.ts +2 -1
- package/featureTypesAndColors.d.ts +19 -6
- package/filterSequenceString.d.ts +14 -10
- package/findApproxMatches.d.ts +7 -1
- package/findNearestRangeOfSequenceOverlapToPosition.d.ts +2 -1
- package/findOrfsInPlasmid.d.ts +2 -11
- package/findSequenceMatches.d.ts +11 -1
- package/generateAnnotations.d.ts +2 -1
- package/generateSequenceData.d.ts +8 -13
- package/getAllInsertionsInSeqReads.d.ts +11 -1
- package/getAminoAcidDataForEachBaseOfDna.d.ts +6 -5
- package/getAminoAcidFromSequenceTriplet.d.ts +1 -1
- package/getAminoAcidStringFromSequenceString.d.ts +3 -1
- package/getCodonRangeForAASliver.d.ts +3 -4
- package/getComplementAminoAcidStringFromSequenceString.d.ts +1 -1
- package/getComplementSequenceAndAnnotations.d.ts +5 -1
- package/getComplementSequenceString.d.ts +1 -1
- package/getCutsiteType.d.ts +2 -1
- package/getCutsitesFromSequence.d.ts +2 -1
- package/getDegenerateDnaStringFromAAString.d.ts +1 -1
- package/getDegenerateRnaStringFromAAString.d.ts +1 -1
- package/getDigestFragmentsForCutsites.d.ts +4 -1
- package/getDigestFragmentsForRestrictionEnzymes.d.ts +8 -1
- package/getInsertBetweenVals.d.ts +2 -1
- package/getLeftAndRightOfSequenceInRangeGivenPosition.d.ts +2 -1
- package/getOrfsFromSequence.d.ts +17 -11
- package/getOverlapBetweenTwoSequences.d.ts +2 -1
- package/getPossiblePartsFromSequenceAndEnzymes.d.ts +18 -1
- package/getReverseAminoAcidStringFromSequenceString.d.ts +1 -1
- package/getReverseComplementAminoAcidStringFromSequenceString.d.ts +1 -1
- package/getReverseComplementAnnotation.d.ts +11 -1
- package/getReverseComplementSequenceAndAnnotations.d.ts +5 -1
- package/getReverseComplementSequenceString.d.ts +1 -1
- package/getReverseSequenceString.d.ts +1 -1
- package/getSequenceDataBetweenRange.d.ts +9 -1
- package/getVirtualDigest.d.ts +11 -10
- package/guessIfSequenceIsDnaAndNotProtein.d.ts +5 -1
- package/index.cjs +732 -483
- package/index.d.ts +8 -5
- package/index.js +732 -483
- package/index.umd.cjs +732 -483
- package/insertGapsIntoRefSeq.d.ts +2 -1
- package/insertSequenceDataAtPositionOrRange.d.ts +10 -1
- package/isEnzymeType2S.d.ts +2 -1
- package/mapAnnotationsToRows.d.ts +9 -1
- package/package.json +9 -6
- package/prepareCircularViewData.d.ts +2 -1
- package/prepareRowData.d.ts +7 -3
- package/proteinAlphabet.d.ts +1 -1
- package/rotateBpsToPosition.d.ts +1 -1
- package/rotateSequenceDataToPosition.d.ts +3 -1
- package/shiftAnnotationsByLen.d.ts +4 -3
- package/src/DNAComplementMap.ts +32 -0
- package/src/addGapsToSeqReads.ts +436 -0
- package/src/adjustAnnotationsToInsert.ts +20 -0
- package/src/adjustBpsToReplaceOrInsert.ts +73 -0
- package/src/aliasedEnzymesByName.ts +7366 -0
- package/src/aminoAcidToDegenerateDnaMap.ts +32 -0
- package/src/aminoAcidToDegenerateRnaMap.ts +32 -0
- package/src/annotateSingleSeq.ts +37 -0
- package/src/annotationTypes.ts +23 -0
- package/src/autoAnnotate.test.js +0 -1
- package/src/autoAnnotate.ts +290 -0
- package/src/bioData.ts +65 -0
- package/src/calculateEndStability.ts +91 -0
- package/src/calculateNebTa.ts +46 -0
- package/src/calculateNebTm.ts +132 -0
- package/src/calculatePercentGC.ts +3 -0
- package/src/calculateSantaLuciaTm.ts +184 -0
- package/src/calculateTm.ts +242 -0
- package/src/computeDigestFragments.ts +238 -0
- package/src/condensePairwiseAlignmentDifferences.ts +85 -0
- package/src/convertAACaretPositionOrRangeToDna.ts +28 -0
- package/src/convertDnaCaretPositionOrRangeToAA.ts +28 -0
- package/src/cutSequenceByRestrictionEnzyme.ts +345 -0
- package/src/defaultEnzymesByName.ts +280 -0
- package/src/degenerateDnaToAminoAcidMap.ts +5 -0
- package/src/degenerateRnaToAminoAcidMap.ts +5 -0
- package/src/deleteSequenceDataAtRange.ts +13 -0
- package/src/diffUtils.ts +80 -0
- package/src/doesEnzymeChopOutsideOfRecognitionSite.ts +16 -0
- package/src/featureTypesAndColors.ts +167 -0
- package/src/filterSequenceString.ts +153 -0
- package/src/findApproxMatches.ts +58 -0
- package/src/findNearestRangeOfSequenceOverlapToPosition.ts +43 -0
- package/src/findOrfsInPlasmid.js +6 -1
- package/src/findOrfsInPlasmid.ts +31 -0
- package/src/findSequenceMatches.ts +154 -0
- package/src/generateAnnotations.ts +39 -0
- package/src/generateSequenceData.ts +212 -0
- package/src/getAllInsertionsInSeqReads.ts +100 -0
- package/src/getAminoAcidDataForEachBaseOfDna.ts +305 -0
- package/src/getAminoAcidFromSequenceTriplet.ts +27 -0
- package/src/getAminoAcidStringFromSequenceString.ts +36 -0
- package/src/getCodonRangeForAASliver.ts +73 -0
- package/src/getComplementAminoAcidStringFromSequenceString.ts +10 -0
- package/src/getComplementSequenceAndAnnotations.ts +25 -0
- package/src/getComplementSequenceString.ts +23 -0
- package/src/getCutsiteType.ts +18 -0
- package/src/getCutsitesFromSequence.ts +22 -0
- package/src/getDegenerateDnaStringFromAAString.ts +15 -0
- package/src/getDegenerateRnaStringFromAAString.ts +15 -0
- package/src/getDigestFragmentsForCutsites.ts +126 -0
- package/src/getDigestFragmentsForRestrictionEnzymes.ts +50 -0
- package/src/getInsertBetweenVals.ts +31 -0
- package/src/getLeftAndRightOfSequenceInRangeGivenPosition.ts +40 -0
- package/src/getMassOfAaString.ts +29 -0
- package/src/getOrfsFromSequence.ts +132 -0
- package/src/getOverlapBetweenTwoSequences.ts +30 -0
- package/src/getPossiblePartsFromSequenceAndEnzymes.ts +149 -0
- package/src/getReverseAminoAcidStringFromSequenceString.ts +22 -0
- package/src/getReverseComplementAminoAcidStringFromSequenceString.ts +10 -0
- package/src/getReverseComplementAnnotation.ts +33 -0
- package/src/getReverseComplementSequenceAndAnnotations.ts +46 -0
- package/src/getReverseComplementSequenceString.ts +18 -0
- package/src/getReverseSequenceString.ts +12 -0
- package/src/getSequenceDataBetweenRange.ts +154 -0
- package/src/getVirtualDigest.ts +139 -0
- package/src/guessIfSequenceIsDnaAndNotProtein.ts +39 -0
- package/src/index.test.ts +43 -0
- package/src/index.ts +111 -0
- package/src/insertGapsIntoRefSeq.ts +43 -0
- package/src/insertSequenceDataAtPosition.ts +2 -0
- package/src/insertSequenceDataAtPositionOrRange.ts +328 -0
- package/src/isEnzymeType2S.ts +5 -0
- package/src/mapAnnotationsToRows.ts +256 -0
- package/src/prepareCircularViewData.ts +24 -0
- package/src/prepareRowData.ts +61 -0
- package/src/prepareRowData_output1.json +1 -0
- package/src/proteinAlphabet.ts +271 -0
- package/src/rotateBpsToPosition.ts +12 -0
- package/src/rotateSequenceDataToPosition.ts +54 -0
- package/src/shiftAnnotationsByLen.ts +24 -0
- package/src/threeLetterSequenceStringToAminoAcidMap.ts +198 -0
- package/src/tidyUpAnnotation.ts +205 -0
- package/src/tidyUpSequenceData.ts +213 -0
- package/src/types.ts +109 -0
- package/threeLetterSequenceStringToAminoAcidMap.d.ts +11 -921
- package/tidyUpAnnotation.d.ts +13 -11
- package/tidyUpSequenceData.d.ts +15 -1
- package/types.d.ts +105 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import getComplementSequenceString from "./getComplementSequenceString";
|
|
2
|
+
import calculatePercentGc from "./calculatePercentGC";
|
|
3
|
+
|
|
4
|
+
// sources of formulas:
|
|
5
|
+
// - https://tmcalculator.neb.com/#!/help
|
|
6
|
+
// - Tm calculation: SantaLucia (1998) PNAS 95:1460-5
|
|
7
|
+
// - salt correction (for monovalent cations): Owczarzy (2004) Biochem 43:3537-54
|
|
8
|
+
|
|
9
|
+
// primer concentration & monovalent cation concentration in M
|
|
10
|
+
|
|
11
|
+
interface NebTmOptions {
|
|
12
|
+
monovalentCationConc?: number;
|
|
13
|
+
primerConc?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function calculateNebTm(
|
|
17
|
+
sequence: string,
|
|
18
|
+
{ monovalentCationConc = 0.05, primerConc = 0.0000005 }: NebTmOptions = {}
|
|
19
|
+
): number | string {
|
|
20
|
+
try {
|
|
21
|
+
const checkForDegenerateBases = /[^atgc]/i.test(sequence);
|
|
22
|
+
if (checkForDegenerateBases) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Degenerate bases prohibited in Tm calculation of sequence ${sequence}`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
const seq = sequence.toUpperCase().split("");
|
|
28
|
+
// enthalpy, entropy
|
|
29
|
+
let h = 0;
|
|
30
|
+
let s = 0;
|
|
31
|
+
// adjustments for helix initiation
|
|
32
|
+
let hi = 0;
|
|
33
|
+
let si = 0;
|
|
34
|
+
// R = universal gas constant with units of cal/(K*mol)
|
|
35
|
+
const r = 1.987;
|
|
36
|
+
// to convert the units of Tm from kelvin to celsius and vice versa
|
|
37
|
+
const kelvinToCelsius = -273.15;
|
|
38
|
+
const celsiusToKelvin = 273.15;
|
|
39
|
+
// to convert the units of enthalpy from kilocal/mol to cal/mol
|
|
40
|
+
const kilocalToCal = 1000;
|
|
41
|
+
const sequenceToEnthalpyMap: Record<string, number> = {
|
|
42
|
+
"AA/TT": -7.9,
|
|
43
|
+
"AT/TA": -7.2,
|
|
44
|
+
"TA/AT": -7.2,
|
|
45
|
+
"CA/GT": -8.5,
|
|
46
|
+
"GT/CA": -8.4,
|
|
47
|
+
"CT/GA": -7.8,
|
|
48
|
+
"GA/CT": -8.2,
|
|
49
|
+
"CG/GC": -10.6,
|
|
50
|
+
"GC/CG": -9.8,
|
|
51
|
+
"GG/CC": -8.0,
|
|
52
|
+
"TT/AA": -7.9,
|
|
53
|
+
"TG/AC": -8.5,
|
|
54
|
+
"AC/TG": -8.4,
|
|
55
|
+
"AG/TC": -7.8,
|
|
56
|
+
"TC/AG": -8.2,
|
|
57
|
+
"CC/GG": -8.0,
|
|
58
|
+
initiationWithTerminalGC: 0.1,
|
|
59
|
+
initiationWithTerminalAT: 2.3
|
|
60
|
+
};
|
|
61
|
+
const sequenceToEntropyMap: Record<string, number> = {
|
|
62
|
+
"AA/TT": -22.2,
|
|
63
|
+
"AT/TA": -20.4,
|
|
64
|
+
"TA/AT": -21.3,
|
|
65
|
+
"CA/GT": -22.7,
|
|
66
|
+
"GT/CA": -22.4,
|
|
67
|
+
"CT/GA": -21.0,
|
|
68
|
+
"GA/CT": -22.2,
|
|
69
|
+
"CG/GC": -27.2,
|
|
70
|
+
"GC/CG": -24.4,
|
|
71
|
+
"GG/CC": -19.9,
|
|
72
|
+
"TT/AA": -22.2,
|
|
73
|
+
"TG/AC": -22.7,
|
|
74
|
+
"AC/TG": -22.4,
|
|
75
|
+
"AG/TC": -21.0,
|
|
76
|
+
"TC/AG": -22.2,
|
|
77
|
+
"CC/GG": -19.9,
|
|
78
|
+
initiationWithTerminalGC: -2.8,
|
|
79
|
+
initiationWithTerminalAT: 4.1
|
|
80
|
+
};
|
|
81
|
+
for (let i = 0; i < seq.length; i++) {
|
|
82
|
+
if (i === 0 || i === seq.length - 1) {
|
|
83
|
+
// first or last nucleotide
|
|
84
|
+
if (seq[i] === "G" || seq[i] === "C") {
|
|
85
|
+
hi += sequenceToEnthalpyMap["initiationWithTerminalGC"];
|
|
86
|
+
si += sequenceToEntropyMap["initiationWithTerminalGC"];
|
|
87
|
+
} else if (seq[i] === "A" || seq[i] === "T") {
|
|
88
|
+
hi += sequenceToEnthalpyMap["initiationWithTerminalAT"];
|
|
89
|
+
si += sequenceToEntropyMap["initiationWithTerminalAT"];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (i < seq.length - 1) {
|
|
93
|
+
const dimer = seq[i] + seq[i + 1];
|
|
94
|
+
const complement = getComplementSequenceString(dimer).toUpperCase();
|
|
95
|
+
const dimerDuplex = `${dimer}/${complement}`;
|
|
96
|
+
if (
|
|
97
|
+
!sequenceToEnthalpyMap[dimerDuplex] ||
|
|
98
|
+
!sequenceToEntropyMap[dimerDuplex]
|
|
99
|
+
) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Could not find value for ${dimerDuplex} of sequence ${sequence}`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
h += sequenceToEnthalpyMap[dimerDuplex];
|
|
105
|
+
s += sequenceToEntropyMap[dimerDuplex];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// this calculated Tm assumes 1 M monovalent cation concentration
|
|
109
|
+
const deltaH = h + hi;
|
|
110
|
+
const deltaS = s + si;
|
|
111
|
+
const numerator = deltaH * kilocalToCal;
|
|
112
|
+
const denominator = deltaS + r * Math.log(primerConc);
|
|
113
|
+
const meltingTemp = numerator / denominator + kelvinToCelsius;
|
|
114
|
+
if (monovalentCationConc) {
|
|
115
|
+
// adjusting Tm for actual monovalent cation concentration
|
|
116
|
+
const lnOfMonoConc = Math.log(monovalentCationConc);
|
|
117
|
+
const gcContent = calculatePercentGc(sequence) / 100;
|
|
118
|
+
const part = 4.29 * gcContent - 3.95;
|
|
119
|
+
const saltCorrection =
|
|
120
|
+
part * Math.pow(10, -5) * lnOfMonoConc +
|
|
121
|
+
Math.pow(9.4, -6) * Math.pow(lnOfMonoConc, 2);
|
|
122
|
+
const adjustedMeltingTemp =
|
|
123
|
+
1 / (1 / (meltingTemp + celsiusToKelvin) + saltCorrection) +
|
|
124
|
+
kelvinToCelsius;
|
|
125
|
+
return adjustedMeltingTemp;
|
|
126
|
+
} else {
|
|
127
|
+
return meltingTemp;
|
|
128
|
+
}
|
|
129
|
+
} catch (err) {
|
|
130
|
+
return `Error calculating Tm for sequence ${sequence}: ${err}`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Primer3 Melting Temperature Calculator
|
|
3
|
+
*
|
|
4
|
+
* Implements the melting temperature calculation algorithm from Primer3
|
|
5
|
+
* based on the documentation at https://primer3.ut.ee/primer3web_help.htm
|
|
6
|
+
*
|
|
7
|
+
* Uses SantaLucia (1998) nearest-neighbor thermodynamics method with
|
|
8
|
+
* fixed Primer3 custom parameters:
|
|
9
|
+
* - Formula: SantaLucia (1998)
|
|
10
|
+
* - Salt correction: SantaLucia (1998)
|
|
11
|
+
* - Monovalent salt: 50.0 mM
|
|
12
|
+
* - Divalent salt: 1.5 mM
|
|
13
|
+
* - dNTP concentration: 0.6 mM
|
|
14
|
+
* - DNA concentration: 50.0 nM
|
|
15
|
+
*
|
|
16
|
+
* References:
|
|
17
|
+
* - SantaLucia JR (1998) "A unified view of polymer, dumbbell and
|
|
18
|
+
* oligonucleotide DNA nearest-neighbor thermodynamics",
|
|
19
|
+
* Proc Natl Acad Sci 95:1460-65
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export interface SantaLuciaParams {
|
|
23
|
+
dH: number;
|
|
24
|
+
dS: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Primer3 custom parameters (fixed)
|
|
28
|
+
const PRIMER3_PARAMS = {
|
|
29
|
+
saltMonovalent: 50.0, // mM
|
|
30
|
+
saltDivalent: 1.5, // mM
|
|
31
|
+
dntpConc: 0.6, // mM
|
|
32
|
+
dnaConc: 50.0, // nM
|
|
33
|
+
R: 1.987 // Gas constant (cal/K·mol)
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// SantaLucia (1998) nearest-neighbor parameters
|
|
37
|
+
// dH in kcal/mol, dS in cal/K·mol
|
|
38
|
+
export const SANTA_LUCIA_NN: Record<string, SantaLuciaParams> = {
|
|
39
|
+
AA: { dH: -7.9, dS: -22.2 },
|
|
40
|
+
TT: { dH: -7.9, dS: -22.2 },
|
|
41
|
+
AT: { dH: -7.2, dS: -20.4 },
|
|
42
|
+
TA: { dH: -7.2, dS: -21.3 },
|
|
43
|
+
CA: { dH: -8.5, dS: -22.7 },
|
|
44
|
+
TG: { dH: -8.5, dS: -22.7 },
|
|
45
|
+
GT: { dH: -8.4, dS: -22.4 },
|
|
46
|
+
AC: { dH: -8.4, dS: -22.4 },
|
|
47
|
+
CT: { dH: -7.8, dS: -21.0 },
|
|
48
|
+
AG: { dH: -7.8, dS: -21.0 },
|
|
49
|
+
GA: { dH: -8.2, dS: -22.2 },
|
|
50
|
+
TC: { dH: -8.2, dS: -22.2 },
|
|
51
|
+
CG: { dH: -10.6, dS: -27.2 },
|
|
52
|
+
GC: { dH: -9.8, dS: -24.4 },
|
|
53
|
+
GG: { dH: -8.0, dS: -19.9 },
|
|
54
|
+
CC: { dH: -8.0, dS: -19.9 }
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Initiation parameters (SantaLucia 1998)
|
|
58
|
+
export const SANTA_LUCIA_INIT: Record<string, SantaLuciaParams> = {
|
|
59
|
+
GC: { dH: 0.1, dS: -2.8 }, // initiation with terminal GC
|
|
60
|
+
AT: { dH: 2.3, dS: 4.1 } // initiation with terminal AT
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Calculate effective monovalent cation concentration
|
|
65
|
+
* Accounts for divalent cations (Mg2+) binding to dNTPs
|
|
66
|
+
* Formula from von Ahsen et al. (2001)
|
|
67
|
+
*
|
|
68
|
+
* @returns {number} - Effective monovalent concentration in mM
|
|
69
|
+
*/
|
|
70
|
+
function getEffectiveMonovalentConc(): number {
|
|
71
|
+
let effectiveMono = PRIMER3_PARAMS.saltMonovalent;
|
|
72
|
+
|
|
73
|
+
// Adjust for divalent cations
|
|
74
|
+
if (PRIMER3_PARAMS.saltDivalent > 0) {
|
|
75
|
+
const freeMg = Math.max(
|
|
76
|
+
0,
|
|
77
|
+
PRIMER3_PARAMS.saltDivalent - PRIMER3_PARAMS.dntpConc
|
|
78
|
+
);
|
|
79
|
+
effectiveMono += 120 * Math.sqrt(freeMg);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return effectiveMono;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Apply SantaLucia (1998) salt correction to entropy
|
|
87
|
+
*
|
|
88
|
+
* @param {number} deltaS - Entropy in cal/K·mol
|
|
89
|
+
* @param {number} nnPairs - Number of nearest-neighbor pairs
|
|
90
|
+
* @returns {number} - Corrected entropy in cal/K·mol
|
|
91
|
+
*/
|
|
92
|
+
function applySaltCorrection(deltaS: number, nnPairs: number): number {
|
|
93
|
+
const effectiveMono = getEffectiveMonovalentConc();
|
|
94
|
+
// SantaLucia (1998) salt correction
|
|
95
|
+
return deltaS + 0.368 * nnPairs * Math.log(effectiveMono / 1000);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Validate DNA sequence
|
|
100
|
+
*
|
|
101
|
+
* @param {string} sequence - DNA sequence
|
|
102
|
+
* @returns {boolean} - True if valid
|
|
103
|
+
*/
|
|
104
|
+
export function isValidSequence(sequence: string): boolean {
|
|
105
|
+
return /^[ATGCN]+$/.test(sequence);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Calculate melting temperature using SantaLucia (1998) method
|
|
110
|
+
*
|
|
111
|
+
* @param {string} sequence - DNA sequence (5' to 3')
|
|
112
|
+
* @returns {number} - Melting temperature in Celsius
|
|
113
|
+
* @throws {Error} Invalid sequence or too short.
|
|
114
|
+
*/
|
|
115
|
+
export default function calculateSantaLuciaTm(
|
|
116
|
+
sequence: string
|
|
117
|
+
): number | string {
|
|
118
|
+
// Convert to uppercase and validate
|
|
119
|
+
try {
|
|
120
|
+
const seq = sequence?.toUpperCase().trim();
|
|
121
|
+
|
|
122
|
+
if (!isValidSequence(seq)) {
|
|
123
|
+
throw new Error("Invalid sequence: contains non-DNA characters");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (seq.length < 2) {
|
|
127
|
+
throw new Error("Sequence too short: minimum length is 2 bases");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let deltaH = 0; // kcal/mol
|
|
131
|
+
let deltaS = 0; // cal/K·mol
|
|
132
|
+
|
|
133
|
+
// Calculate nearest-neighbor contributions
|
|
134
|
+
for (let i = 0; i < seq.length - 1; i++) {
|
|
135
|
+
const dinucleotide = seq.substring(i, i + 2);
|
|
136
|
+
|
|
137
|
+
// Skip if contains N
|
|
138
|
+
if (dinucleotide.includes("N")) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const params = SANTA_LUCIA_NN[dinucleotide];
|
|
143
|
+
if (params) {
|
|
144
|
+
deltaH += params.dH;
|
|
145
|
+
deltaS += params.dS;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Add initiation parameters
|
|
150
|
+
const firstBase = seq[0];
|
|
151
|
+
const lastBase = seq[seq.length - 1];
|
|
152
|
+
|
|
153
|
+
// Terminal GC or AT initiation
|
|
154
|
+
if (firstBase === "G" || firstBase === "C") {
|
|
155
|
+
deltaH += SANTA_LUCIA_INIT["GC"].dH;
|
|
156
|
+
deltaS += SANTA_LUCIA_INIT["GC"].dS;
|
|
157
|
+
} else {
|
|
158
|
+
deltaH += SANTA_LUCIA_INIT["AT"].dH;
|
|
159
|
+
deltaS += SANTA_LUCIA_INIT["AT"].dS;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (lastBase === "G" || lastBase === "C") {
|
|
163
|
+
deltaH += SANTA_LUCIA_INIT["GC"].dH;
|
|
164
|
+
deltaS += SANTA_LUCIA_INIT["GC"].dS;
|
|
165
|
+
} else {
|
|
166
|
+
deltaH += SANTA_LUCIA_INIT["AT"].dH;
|
|
167
|
+
deltaS += SANTA_LUCIA_INIT["AT"].dS;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Apply salt correction
|
|
171
|
+
const nnPairs = seq.length - 1;
|
|
172
|
+
deltaS = applySaltCorrection(deltaS, nnPairs);
|
|
173
|
+
|
|
174
|
+
// Calculate Tm using: Tm = deltaH / (deltaS + R * ln(C/4))
|
|
175
|
+
// where C is DNA concentration in M (convert from nM)
|
|
176
|
+
const C = PRIMER3_PARAMS.dnaConc * 1e-9; // Convert nM to M
|
|
177
|
+
const Tm = (deltaH * 1000) / (deltaS + PRIMER3_PARAMS.R * Math.log(C / 4));
|
|
178
|
+
|
|
179
|
+
// Convert from Kelvin to Celsius
|
|
180
|
+
return Tm - 273.15;
|
|
181
|
+
} catch (e) {
|
|
182
|
+
return `Error calculating Tm for sequence ${sequence}. ${e}`;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/* eslint-disable eqeqeq */
|
|
2
|
+
/**
|
|
3
|
+
* DNA melting temperature calculator.
|
|
4
|
+
* @author Nick Elsbree
|
|
5
|
+
* @author Zinovii Dmytriv (original author)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
interface CalculateTemperatureOptions {
|
|
9
|
+
type?: "breslauer" | "sugimoto" | "unified";
|
|
10
|
+
A?: number;
|
|
11
|
+
R?: number;
|
|
12
|
+
primerConc?: number;
|
|
13
|
+
monovalentCationConc?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const calcTmMethods = {
|
|
17
|
+
TABLE_BRESLAUER: "breslauer" as const,
|
|
18
|
+
TABLE_SUGIMOTO: "sugimoto" as const,
|
|
19
|
+
TABLE_UNIFIED: "unified" as const,
|
|
20
|
+
|
|
21
|
+
A: -10.8, // Helix initiation for deltaS
|
|
22
|
+
R: 1.987, // Gas constant (cal/(K*mol)).
|
|
23
|
+
primerConc: 0.0000005, // Oligo concentration. 0.5uM is typical for PCR.
|
|
24
|
+
monovalentCationConc: 0.05, // Monovalent salt concentration. 50mM is typical for PCR.
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Calculates temperature for DNA sequence using a given algorithm.
|
|
28
|
+
* sequence - The DNA sequence to use.
|
|
29
|
+
* type - Either Teselagen.bio.tools.TemperatureCalculator.TABLE_BRESLAUER, TABLE_SUGIMOTO, or TABLE_UNIFIED
|
|
30
|
+
* A - Helix initation for deltaS. Defaults to -10.8.
|
|
31
|
+
* R - The gas constant, in cal/(K*mol). Defaults to 0.5e-6M.
|
|
32
|
+
* monovalentCationConc - THe monovalent salt concentration. Defaults to 50e-3M.
|
|
33
|
+
* return - Temperature for the given sequence, in Celsius.
|
|
34
|
+
*/
|
|
35
|
+
calculateTemperature: function (
|
|
36
|
+
_sequence: string,
|
|
37
|
+
{
|
|
38
|
+
type,
|
|
39
|
+
A,
|
|
40
|
+
R,
|
|
41
|
+
primerConc,
|
|
42
|
+
monovalentCationConc
|
|
43
|
+
}: CalculateTemperatureOptions = {}
|
|
44
|
+
): number {
|
|
45
|
+
const sequence = (_sequence || "").toLowerCase();
|
|
46
|
+
if (typeof type === "undefined") {
|
|
47
|
+
// type = this.TABLE_SUGIMOTO ;
|
|
48
|
+
// type = this.TABLE_UNIFIED;
|
|
49
|
+
type = this.TABLE_BRESLAUER;
|
|
50
|
+
} else if (
|
|
51
|
+
type !== this.TABLE_BRESLAUER &&
|
|
52
|
+
type !== this.TABLE_UNIFIED &&
|
|
53
|
+
type !== this.TABLE_SUGIMOTO
|
|
54
|
+
) {
|
|
55
|
+
throw new Error("Invalid table type!");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* eslint-disable no-param-reassign */
|
|
59
|
+
if (!A) {
|
|
60
|
+
A = this.A;
|
|
61
|
+
}
|
|
62
|
+
if (!R) {
|
|
63
|
+
R = this.R;
|
|
64
|
+
}
|
|
65
|
+
if (!primerConc) {
|
|
66
|
+
primerConc = this.primerConc;
|
|
67
|
+
}
|
|
68
|
+
if (!monovalentCationConc) {
|
|
69
|
+
monovalentCationConc = this.monovalentCationConc;
|
|
70
|
+
}
|
|
71
|
+
/* eslint-enable no-param-reassign */
|
|
72
|
+
|
|
73
|
+
const sequenceLength = sequence.length;
|
|
74
|
+
|
|
75
|
+
if (sequenceLength === 0) {
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const deltaHTable = this.getDeltaHTable(type);
|
|
80
|
+
const deltaSTable = this.getDeltaSTable(type);
|
|
81
|
+
|
|
82
|
+
if (!deltaHTable || !deltaSTable) {
|
|
83
|
+
// Should not happen if type validation passes
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const neighbors: number[] = []; // List goes in order: aa, at, ac, ag, tt, ta, tc, tg, cc, ca, ct, cg, gg, ga, gt, gc
|
|
88
|
+
|
|
89
|
+
neighbors.push(this.calculateReps(sequence, "aa"));
|
|
90
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "at"));
|
|
91
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "ac"));
|
|
92
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "ag"));
|
|
93
|
+
|
|
94
|
+
neighbors.push(this.calculateReps(sequence, "tt"));
|
|
95
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "ta"));
|
|
96
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "tc"));
|
|
97
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "tg"));
|
|
98
|
+
|
|
99
|
+
neighbors.push(this.calculateReps(sequence, "cc"));
|
|
100
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "ca"));
|
|
101
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "ct"));
|
|
102
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "cg"));
|
|
103
|
+
|
|
104
|
+
neighbors.push(this.calculateReps(sequence, "gg"));
|
|
105
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "ga"));
|
|
106
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "gt"));
|
|
107
|
+
neighbors.push(this.calculateNumberOfOccurrences(sequence, "gc"));
|
|
108
|
+
|
|
109
|
+
let sumDeltaH = 0.0;
|
|
110
|
+
let sumDeltaS = 0.0;
|
|
111
|
+
|
|
112
|
+
for (let i = 0; i < 16; i++) {
|
|
113
|
+
// Safe access as neighbors and tables are 16-length
|
|
114
|
+
sumDeltaH = sumDeltaH + neighbors[i] * deltaHTable[i];
|
|
115
|
+
sumDeltaS = sumDeltaS + neighbors[i] * deltaSTable[i];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const temperature =
|
|
119
|
+
(-1000.0 * sumDeltaH) /
|
|
120
|
+
(A + -sumDeltaS + R * Math.log(primerConc / 4.0)) -
|
|
121
|
+
273.15 +
|
|
122
|
+
16.6 * Math.LOG10E * Math.log(monovalentCationConc);
|
|
123
|
+
|
|
124
|
+
return temperature;
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @private
|
|
129
|
+
* Function to return deltaH table for given algorithm.
|
|
130
|
+
* @param {String} type Algorithm to get table for.
|
|
131
|
+
* @return {Number[]} deltaH table for given algorithm.
|
|
132
|
+
*/
|
|
133
|
+
getDeltaHTable: function (type: string): number[] | null {
|
|
134
|
+
if (type === this.TABLE_BRESLAUER) {
|
|
135
|
+
return [
|
|
136
|
+
9.1, 8.6, 6.5, 7.8, 9.1, 6.0, 5.6, 5.8, 11.0, 5.8, 7.8, 11.9, 11.0, 5.6,
|
|
137
|
+
6.5, 11.1
|
|
138
|
+
];
|
|
139
|
+
} else if (type === this.TABLE_SUGIMOTO) {
|
|
140
|
+
return [
|
|
141
|
+
8.0, 5.6, 6.5, 7.8, 8.0, 5.6, 5.6, 5.8, 10.9, 8.2, 6.6, 11.8, 10.9, 6.6,
|
|
142
|
+
9.4, 11.9
|
|
143
|
+
];
|
|
144
|
+
} else if (type === this.TABLE_UNIFIED) {
|
|
145
|
+
return [
|
|
146
|
+
7.9, 7.2, 8.4, 7.8, 7.9, 7.2, 8.2, 8.5, 8.0, 8.5, 7.8, 10.6, 8.0, 8.2,
|
|
147
|
+
8.4, 9.8
|
|
148
|
+
];
|
|
149
|
+
} else {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @private
|
|
156
|
+
* Function to return deltaS table for given algorithm.
|
|
157
|
+
* @param {String} type Algorithm to get table for.
|
|
158
|
+
* @return {Number[]} deltaS table for given algorithm.
|
|
159
|
+
*/
|
|
160
|
+
getDeltaSTable: function (type: string): number[] | null {
|
|
161
|
+
if (type === this.TABLE_BRESLAUER) {
|
|
162
|
+
return [
|
|
163
|
+
24.0, 23.9, 17.3, 20.8, 24.0, 16.9, 13.5, 12.9, 26.6, 12.9, 20.8, 27.8,
|
|
164
|
+
26.6, 13.5, 17.3, 26.7
|
|
165
|
+
];
|
|
166
|
+
} else if (type === this.TABLE_SUGIMOTO) {
|
|
167
|
+
return [
|
|
168
|
+
21.9, 15.2, 17.3, 20.8, 21.9, 15.2, 13.5, 12.9, 28.4, 25.5, 23.5, 29.0,
|
|
169
|
+
28.4, 16.4, 25.5, 29.0
|
|
170
|
+
];
|
|
171
|
+
} else if (type === this.TABLE_UNIFIED) {
|
|
172
|
+
return [
|
|
173
|
+
22.2, 20.4, 22.4, 21.0, 22.2, 21.3, 22.2, 22.7, 19.9, 22.7, 21.0, 27.2,
|
|
174
|
+
19.9, 22.2, 22.4, 24.4
|
|
175
|
+
];
|
|
176
|
+
} else {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @private
|
|
183
|
+
* Finds number of occurrences of target in sequence.
|
|
184
|
+
* Will find repeating sequences, meaning that
|
|
185
|
+
* calculateReps("aaa", "aa") returns 2 rather than 1.
|
|
186
|
+
* @param {String} sequence The string to search through.
|
|
187
|
+
* @param {String} target The string to search for.
|
|
188
|
+
* @return {Int} Number of occurrences of target in sequence, with repeats.
|
|
189
|
+
*/
|
|
190
|
+
calculateReps: function (sequence: string, target: string): number {
|
|
191
|
+
const sequenceLength = sequence.length;
|
|
192
|
+
|
|
193
|
+
if (sequenceLength === 0) {
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let numFound = 0;
|
|
198
|
+
let seqOffset = 0; // Search offset for finding multiple matches.
|
|
199
|
+
|
|
200
|
+
// eslint-disable-next-line no-constant-condition
|
|
201
|
+
while (true) {
|
|
202
|
+
const foundSeq = sequence.indexOf(target, seqOffset);
|
|
203
|
+
|
|
204
|
+
if (foundSeq === -1) {
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
seqOffset = foundSeq + 1;
|
|
209
|
+
numFound++;
|
|
210
|
+
|
|
211
|
+
if (seqOffset > sequenceLength) {
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return numFound;
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @private
|
|
221
|
+
* Counts number of occurrences of target in sequence, without repeating.
|
|
222
|
+
* @param {String} sequence The string to search through.
|
|
223
|
+
* @param {String} target The string to search for.
|
|
224
|
+
* @return {Int} Number of occurrences of target in sequence.
|
|
225
|
+
*/
|
|
226
|
+
calculateNumberOfOccurrences: function (
|
|
227
|
+
sequence: string,
|
|
228
|
+
target: string
|
|
229
|
+
): number {
|
|
230
|
+
const sequenceLength = sequence.length;
|
|
231
|
+
|
|
232
|
+
if (sequenceLength === 0) {
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const numberFound = sequence.split(target).length - 1;
|
|
237
|
+
|
|
238
|
+
return numberFound;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export default calcTmMethods.calculateTemperature.bind(calcTmMethods);
|