@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.
Files changed (168) hide show
  1. package/DNAComplementMap.d.ts +1 -1
  2. package/addGapsToSeqReads.d.ts +16 -3
  3. package/adjustAnnotationsToInsert.d.ts +2 -1
  4. package/adjustBpsToReplaceOrInsert.d.ts +2 -1
  5. package/aliasedEnzymesByName.d.ts +37 -1
  6. package/aminoAcidToDegenerateDnaMap.d.ts +1 -31
  7. package/aminoAcidToDegenerateRnaMap.d.ts +1 -1
  8. package/annotateSingleSeq.d.ts +5 -4
  9. package/annotationTypes.d.ts +2 -2
  10. package/autoAnnotate.d.ts +17 -8
  11. package/bioData.d.ts +10 -58
  12. package/calculateEndStability.d.ts +1 -1
  13. package/calculateNebTa.d.ts +6 -1
  14. package/calculateNebTm.d.ts +6 -4
  15. package/calculatePercentGC.d.ts +1 -1
  16. package/calculateSantaLuciaTm.d.ts +28 -114
  17. package/calculateTm.d.ts +13 -1
  18. package/computeDigestFragments.d.ts +30 -24
  19. package/condensePairwiseAlignmentDifferences.d.ts +1 -1
  20. package/convertAACaretPositionOrRangeToDna.d.ts +2 -1
  21. package/convertDnaCaretPositionOrRangeToAA.d.ts +2 -1
  22. package/cutSequenceByRestrictionEnzyme.d.ts +2 -1
  23. package/defaultEnzymesByName.d.ts +2 -1
  24. package/degenerateDnaToAminoAcidMap.d.ts +1 -1
  25. package/degenerateRnaToAminoAcidMap.d.ts +1 -1
  26. package/deleteSequenceDataAtRange.d.ts +2 -1
  27. package/diffUtils.d.ts +9 -7
  28. package/doesEnzymeChopOutsideOfRecognitionSite.d.ts +2 -1
  29. package/featureTypesAndColors.d.ts +19 -6
  30. package/filterSequenceString.d.ts +14 -10
  31. package/findApproxMatches.d.ts +7 -1
  32. package/findNearestRangeOfSequenceOverlapToPosition.d.ts +2 -1
  33. package/findOrfsInPlasmid.d.ts +2 -11
  34. package/findSequenceMatches.d.ts +11 -1
  35. package/generateAnnotations.d.ts +2 -1
  36. package/generateSequenceData.d.ts +8 -13
  37. package/getAllInsertionsInSeqReads.d.ts +11 -1
  38. package/getAminoAcidDataForEachBaseOfDna.d.ts +6 -5
  39. package/getAminoAcidFromSequenceTriplet.d.ts +1 -1
  40. package/getAminoAcidStringFromSequenceString.d.ts +3 -1
  41. package/getCodonRangeForAASliver.d.ts +3 -4
  42. package/getComplementAminoAcidStringFromSequenceString.d.ts +1 -1
  43. package/getComplementSequenceAndAnnotations.d.ts +5 -1
  44. package/getComplementSequenceString.d.ts +1 -1
  45. package/getCutsiteType.d.ts +2 -1
  46. package/getCutsitesFromSequence.d.ts +2 -1
  47. package/getDegenerateDnaStringFromAAString.d.ts +1 -1
  48. package/getDegenerateRnaStringFromAAString.d.ts +1 -1
  49. package/getDigestFragmentsForCutsites.d.ts +4 -1
  50. package/getDigestFragmentsForRestrictionEnzymes.d.ts +8 -1
  51. package/getInsertBetweenVals.d.ts +2 -1
  52. package/getLeftAndRightOfSequenceInRangeGivenPosition.d.ts +2 -1
  53. package/getOrfsFromSequence.d.ts +17 -11
  54. package/getOverlapBetweenTwoSequences.d.ts +2 -1
  55. package/getPossiblePartsFromSequenceAndEnzymes.d.ts +18 -1
  56. package/getReverseAminoAcidStringFromSequenceString.d.ts +1 -1
  57. package/getReverseComplementAminoAcidStringFromSequenceString.d.ts +1 -1
  58. package/getReverseComplementAnnotation.d.ts +11 -1
  59. package/getReverseComplementSequenceAndAnnotations.d.ts +5 -1
  60. package/getReverseComplementSequenceString.d.ts +1 -1
  61. package/getReverseSequenceString.d.ts +1 -1
  62. package/getSequenceDataBetweenRange.d.ts +9 -1
  63. package/getVirtualDigest.d.ts +11 -10
  64. package/guessIfSequenceIsDnaAndNotProtein.d.ts +5 -1
  65. package/index.cjs +732 -483
  66. package/index.d.ts +8 -5
  67. package/index.js +732 -483
  68. package/index.umd.cjs +732 -483
  69. package/insertGapsIntoRefSeq.d.ts +2 -1
  70. package/insertSequenceDataAtPositionOrRange.d.ts +10 -1
  71. package/isEnzymeType2S.d.ts +2 -1
  72. package/mapAnnotationsToRows.d.ts +9 -1
  73. package/package.json +9 -6
  74. package/prepareCircularViewData.d.ts +2 -1
  75. package/prepareRowData.d.ts +7 -3
  76. package/proteinAlphabet.d.ts +1 -1
  77. package/rotateBpsToPosition.d.ts +1 -1
  78. package/rotateSequenceDataToPosition.d.ts +3 -1
  79. package/shiftAnnotationsByLen.d.ts +4 -3
  80. package/src/DNAComplementMap.ts +32 -0
  81. package/src/addGapsToSeqReads.ts +436 -0
  82. package/src/adjustAnnotationsToInsert.ts +20 -0
  83. package/src/adjustBpsToReplaceOrInsert.ts +73 -0
  84. package/src/aliasedEnzymesByName.ts +7366 -0
  85. package/src/aminoAcidToDegenerateDnaMap.ts +32 -0
  86. package/src/aminoAcidToDegenerateRnaMap.ts +32 -0
  87. package/src/annotateSingleSeq.ts +37 -0
  88. package/src/annotationTypes.ts +23 -0
  89. package/src/autoAnnotate.test.js +0 -1
  90. package/src/autoAnnotate.ts +290 -0
  91. package/src/bioData.ts +65 -0
  92. package/src/calculateEndStability.ts +91 -0
  93. package/src/calculateNebTa.ts +46 -0
  94. package/src/calculateNebTm.ts +132 -0
  95. package/src/calculatePercentGC.ts +3 -0
  96. package/src/calculateSantaLuciaTm.ts +184 -0
  97. package/src/calculateTm.ts +242 -0
  98. package/src/computeDigestFragments.ts +238 -0
  99. package/src/condensePairwiseAlignmentDifferences.ts +85 -0
  100. package/src/convertAACaretPositionOrRangeToDna.ts +28 -0
  101. package/src/convertDnaCaretPositionOrRangeToAA.ts +28 -0
  102. package/src/cutSequenceByRestrictionEnzyme.ts +345 -0
  103. package/src/defaultEnzymesByName.ts +280 -0
  104. package/src/degenerateDnaToAminoAcidMap.ts +5 -0
  105. package/src/degenerateRnaToAminoAcidMap.ts +5 -0
  106. package/src/deleteSequenceDataAtRange.ts +13 -0
  107. package/src/diffUtils.ts +80 -0
  108. package/src/doesEnzymeChopOutsideOfRecognitionSite.ts +16 -0
  109. package/src/featureTypesAndColors.ts +167 -0
  110. package/src/filterSequenceString.ts +153 -0
  111. package/src/findApproxMatches.ts +58 -0
  112. package/src/findNearestRangeOfSequenceOverlapToPosition.ts +43 -0
  113. package/src/findOrfsInPlasmid.js +6 -1
  114. package/src/findOrfsInPlasmid.ts +31 -0
  115. package/src/findSequenceMatches.ts +154 -0
  116. package/src/generateAnnotations.ts +39 -0
  117. package/src/generateSequenceData.ts +212 -0
  118. package/src/getAllInsertionsInSeqReads.ts +100 -0
  119. package/src/getAminoAcidDataForEachBaseOfDna.ts +305 -0
  120. package/src/getAminoAcidFromSequenceTriplet.ts +27 -0
  121. package/src/getAminoAcidStringFromSequenceString.ts +36 -0
  122. package/src/getCodonRangeForAASliver.ts +73 -0
  123. package/src/getComplementAminoAcidStringFromSequenceString.ts +10 -0
  124. package/src/getComplementSequenceAndAnnotations.ts +25 -0
  125. package/src/getComplementSequenceString.ts +23 -0
  126. package/src/getCutsiteType.ts +18 -0
  127. package/src/getCutsitesFromSequence.ts +22 -0
  128. package/src/getDegenerateDnaStringFromAAString.ts +15 -0
  129. package/src/getDegenerateRnaStringFromAAString.ts +15 -0
  130. package/src/getDigestFragmentsForCutsites.ts +126 -0
  131. package/src/getDigestFragmentsForRestrictionEnzymes.ts +50 -0
  132. package/src/getInsertBetweenVals.ts +31 -0
  133. package/src/getLeftAndRightOfSequenceInRangeGivenPosition.ts +40 -0
  134. package/src/getMassOfAaString.ts +29 -0
  135. package/src/getOrfsFromSequence.ts +132 -0
  136. package/src/getOverlapBetweenTwoSequences.ts +30 -0
  137. package/src/getPossiblePartsFromSequenceAndEnzymes.ts +149 -0
  138. package/src/getReverseAminoAcidStringFromSequenceString.ts +22 -0
  139. package/src/getReverseComplementAminoAcidStringFromSequenceString.ts +10 -0
  140. package/src/getReverseComplementAnnotation.ts +33 -0
  141. package/src/getReverseComplementSequenceAndAnnotations.ts +46 -0
  142. package/src/getReverseComplementSequenceString.ts +18 -0
  143. package/src/getReverseSequenceString.ts +12 -0
  144. package/src/getSequenceDataBetweenRange.ts +154 -0
  145. package/src/getVirtualDigest.ts +139 -0
  146. package/src/guessIfSequenceIsDnaAndNotProtein.ts +39 -0
  147. package/src/index.test.ts +43 -0
  148. package/src/index.ts +111 -0
  149. package/src/insertGapsIntoRefSeq.ts +43 -0
  150. package/src/insertSequenceDataAtPosition.ts +2 -0
  151. package/src/insertSequenceDataAtPositionOrRange.ts +328 -0
  152. package/src/isEnzymeType2S.ts +5 -0
  153. package/src/mapAnnotationsToRows.ts +256 -0
  154. package/src/prepareCircularViewData.ts +24 -0
  155. package/src/prepareRowData.ts +61 -0
  156. package/src/prepareRowData_output1.json +1 -0
  157. package/src/proteinAlphabet.ts +271 -0
  158. package/src/rotateBpsToPosition.ts +12 -0
  159. package/src/rotateSequenceDataToPosition.ts +54 -0
  160. package/src/shiftAnnotationsByLen.ts +24 -0
  161. package/src/threeLetterSequenceStringToAminoAcidMap.ts +198 -0
  162. package/src/tidyUpAnnotation.ts +205 -0
  163. package/src/tidyUpSequenceData.ts +213 -0
  164. package/src/types.ts +109 -0
  165. package/threeLetterSequenceStringToAminoAcidMap.d.ts +11 -921
  166. package/tidyUpAnnotation.d.ts +13 -11
  167. package/tidyUpSequenceData.d.ts +15 -1
  168. 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,3 @@
1
+ export default function calculatePercentGC(bps: string): number {
2
+ return ((bps.match(/[cg]/gi) || []).length / bps.length) * 100 || 0;
3
+ }
@@ -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);