The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

TmCalculator 1.1.2

Which releases returned wrong melting temperatures, and who needs to act

tm_nn() scored duplexes that are not perfectly paired incorrectly in every release from 1.0.5 (2026-06-04) through 1.1.1 (2026-09-21) — 1.0.5, 1.0.6, 1.0.7, 1.0.8, 1.0.9, 1.1.0 and 1.1.1. Versions up to and including 1.0.4 are correct, and so is 1.1.2: on the duplex from issue #8 both give 59.973 °C, while the affected releases give 62.92 or 61.63 depending on which strand was passed.

You are affected only if you supplied complement_seq to to_genomic_ranges() or tm_calculate(), or a non-zero shift to tm_nn(). Those are the only ways a mismatch or a dangling end reaches the calculation.

You are not affected if the complement was generated for you, which is what happens for every sequence, FASTA, BSgenome and GRanges input where you did not pass one yourself. Those duplexes are perfectly paired, every value is identical to 1.1.1, and nothing needs recomputing.

To check an object you already have:

mc <- GenomicRanges::mcols(gr)
sum(as.character(mc$complement) !=
      generate_complement(as.character(mc$sequence)))
#  0 -> every duplex is perfectly paired; your results are unaffected
#  >0 -> that many duplexes carry a mismatch; recompute them with 1.1.2

How far off the affected releases were, over 400 random 18-24-mers: up to about 4 °C for one internal mismatch, about 16 °C for a terminal mismatch, and about 6 °C for two mismatches. The error is not a constant offset and does not average out: the same molecule gave two different answers depending on which strand was passed as sequence, differing by up to about 3 °C.

Bug fix affecting every mismatched duplex

This is a regression, not an original defect. Up to 1.0.4 the stacking loop was an explicit if key / else if reversed key / else if other table / else stop() chain: it retried the reversed spelling of a key, took one table or the other rather than both, and refused to guess at a stack no table defined.

All three properties were lost in a single commit on 2026-05-26, which vectorised that per-base loop and, in the same change, replaced the retry with a completed parameter table — .complete_nn_rc(), which fills in the six reverse-orientation rows the published nearest-neighbor tables omit. The completion was applied to the nearest-neighbor tables only. The internal-mismatch, terminal-mismatch and dangling-end tables were left in one orientation with nothing left to look up the other, which is why perfectly paired duplexes came through untouched and every mismatch did not — and why the fault survived seven releases, since every existing test used perfectly paired duplexes.

1.1.2 restores all three, and corrects the terminal-key orientation, which was wrong in both the old and the new form.

Bug fix affecting every RNA/DNA hybrid Tm

Bug fix affecting DNA_NN_Breslauer_1986

What the initiation terms are charged on

Not a change in 1.1.2, but it is now stated in ?tm_nn rather than left implicit, because it is the one place where this package deliberately parts company with Biopython.

A dangling end or terminal mismatch is covered by its own parameter and the position is then consumed; the initiation terms are charged on what remains. Biopython consumes the position for the stacking sum but still indexes all four initiation terms on the sequence as supplied. The consequence is that its answer depends on which strand you hand over:

# the same duplex, written from either strand, with one terminal A-C mismatch
tm_calculate("AGCGCGCGCA", complement_seq = "CCGCGCGCGT",
             nn_table = "DNA_NN_SantaLucia_2004",
             dnac_high = 250, dnac_low = 0, salt_method = "none")   # 63.6925
tm_calculate("TGCGCGCGCC", complement_seq = "ACGCGCGCGA",
             nn_table = "DNA_NN_SantaLucia_2004",
             dnac_high = 250, dnac_low = 0, salt_method = "none")   # 63.6925

Biopython returns 64.2325 and 63.6925 for those two. Every other term is identical — the terminal-mismatch parameter is CC/GA either way and the eight stacks sum to the same value. The whole difference is one init_A/T traded for one init_G/C, 2.2 kcal/mol and 6.9 e.u.

The sharpest form of the argument is the dangling-end case, which is @haraldn’s (#8). At a terminal mismatch two bases still face each other, so which pair closes the duplex is at least arguable. At a dangling end the outermost residue has no partner at all, and a residue that is in no base pair cannot carry the penalty for a terminal base pair under any reading of SantaLucia & Hicks (2004). "GCATGCATGA" against "CGTACGTAC" takes the .C/AG dangling-end term, trims, and Biopython then charges a full init_A/T on the unpaired A purely because it is seq[-1]; this package returns 44.2801 where Biopython returns 44.2344.

How large the divergence is depends on where the duplex melts. init_A/T is (2.2 kcal/mol, 6.9 e.u.), so its ratio is 318.8 K: its effect on Tm passes through zero near 45.7 °C and changes sign either side. That is why the same disagreement is worth 0.05 °C on the duplex above, which melts at 44.3 °C, and 0.37 °C on a 16-mer melting further away.

DNA_NN_Breslauer_1986 is not bound by that cancellation, because the row that diverges for it is init_allA/T rather than init_A/T — the two conventions can disagree about whether the duplex contains a G·C pair at all. That needs the single G or C to be the mismatched terminal base itself, so that trimming removes it: for "GATATATATATATATA" against "ATATATATATATATAT" the gap is 3.0 °C, and it widens to 4.9 °C at 8 nt, where the duplex no longer melts above 0 °C. This is a different thing from the gc_ends defect described in the previous section, which was our own and needed no mismatch at all.

Smaller things

Strand direction, which is what invites the mistake

Bug fix affecting Owczarzy2008 with magnesium

Breaking change in tm_gc()

Behaviour changes

TmCalculator 1.1.1

New features

Bug fix affecting all nearest-neighbor Tm values

Breaking changes

Performance

Dependencies

Bug fixes

Known issues

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.