Skip to content

core: fix TabulatedPotential min==max yielding invstepsize=NaN (bug-sweep #44)#5369

Draft
RudolfWeeber wants to merge 1 commit into
espressomd:pythonfrom
RudolfWeeber:fix/bug-44-tabulated-min-max-nan
Draft

core: fix TabulatedPotential min==max yielding invstepsize=NaN (bug-sweep #44)#5369
RudolfWeeber wants to merge 1 commit into
espressomd:pythonfrom
RudolfWeeber:fix/bug-44-tabulated-min-max-nan

Conversation

@RudolfWeeber

Copy link
Copy Markdown
Contributor

A single-point tabulated potential (minval == maxval, force.size() == 1) is
an intentionally supported, constant potential: the constructor only throws
when the force table does not hold exactly one element in this case
(mirrored by testsuite/python/tabulated.py). However the constructor then
computed invstepsize = (size - 1) / (maxval - minval) = 0/0 = NaN, which
poisoned every force()/energy() evaluation and triggered an out-of-bounds
read of the size-1 table in Utils::linear_interpolation
(static_cast(NaN) is undefined, table[uind + 1] reads element [1]).

Fix:

  • TabulatedPotential.cpp: guard the invstepsize computation so the
    degenerate denominator yields invstepsize = 0 (mirroring the existing
    minval == -1. sentinel branch). With invstepsize = 0 the interpolation
    collapses to the single constant table value, which is physically correct
    for a one-point table.
  • linear_interpolation.hpp: harden the residual latent out-of-bounds read so
    the upper data point is never accessed past the end of a single-point table
    (the term is multiplied by dx == 0 anyway).

Adds src/core/unit_tests/TabulatedPotential_test.cpp covering the degenerate
single-point table and a regular multi-point table.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

🤖 Generated with Claude Code

…weep #44)

A single-point tabulated potential (minval == maxval, force.size() == 1) is
an intentionally supported, constant potential: the constructor only throws
when the force table does not hold exactly one element in this case
(mirrored by testsuite/python/tabulated.py). However the constructor then
computed invstepsize = (size - 1) / (maxval - minval) = 0/0 = NaN, which
poisoned every force()/energy() evaluation and triggered an out-of-bounds
read of the size-1 table in Utils::linear_interpolation
(static_cast<int>(NaN) is undefined, table[uind + 1] reads element [1]).

Fix:
- TabulatedPotential.cpp: guard the invstepsize computation so the
  degenerate denominator yields invstepsize = 0 (mirroring the existing
  minval == -1. sentinel branch). With invstepsize = 0 the interpolation
  collapses to the single constant table value, which is physically correct
  for a one-point table.
- linear_interpolation.hpp: harden the residual latent out-of-bounds read so
  the upper data point is never accessed past the end of a single-point table
  (the term is multiplied by dx == 0 anyway).

Adds src/core/unit_tests/TabulatedPotential_test.cpp covering the degenerate
single-point table and a regular multi-point table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant