Skip to content

[pull] develop from lammps:develop - #169

Merged
pull[bot] merged 137 commits into
comphy-lab:developfrom
lammps:develop
Jul 11, 2026
Merged

[pull] develop from lammps:develop#169
pull[bot] merged 137 commits into
comphy-lab:developfrom
lammps:develop

Conversation

@pull

@pull pull Bot commented Jul 11, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

akohlmey and others added 30 commits July 3, 2026 05:04
A typo (jm1 = j = 1 instead of jm1 = j - 1, as used in the three
parallel branches) made 2d grid averaging of a single bracketed
per-atom value like c_ID[3] silently accumulate column 2 instead
of the requested column, and clobbered the value index j.

Found while triaging Coverity scan defects (no CID; discovered by
inspection of neighboring flagged code paths).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
The selection-sort for angleneigh executed its swap statements on
every inner iteration (outside the improvement test), unlike the
correct bondneigh twin loop directly above. With a stale swap index
this duplicates the running minimum and drops an element for most
input orderings, so the 1-3 special neighbor list used for Tinker-
convention multipole frame assignment could be corrupted, yielding
wrong AMOEBA/HIPPO frames and forces for unlucky atom-ID orderings.

Coverity CID 1661393 (uninitialized index read); the real defect is
the misplaced braces.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
In pair styles lj/cut/tip4p/long/omp and lj/long/tip4p/long/omp the
bookkeeping for ev_tally_list_thr() (the n/key counters and the vlist
atom-index list) was only maintained when the virial was requested
(VFLAG resp. a runtime vflag check), but ev_tally_list_thr() is called
whenever EVFLAG is set and uses key/vlist to distribute the pair energy
over the 2/4/6 listed atoms when eflag_atom is set. With per-atom energy
requested but no virial (e.g. compute pe/atom without stress output)
the tally indexed eatom_pair[] with uninitialized garbage indices,
causing out-of-bounds writes.

Restructure to match the base classes and the other five TIP4P OMP
variants: maintain n/key/vlist under EVFLAG, keep only the v[] virial
component computation under the virial condition.

Coverity CIDs 1662776, 1661025, 1662344, 1661588, 1662877.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
Pair::init_tables() left the espflag branch of the table-parameter
dispatch empty and had no espflag case in the cut_respa table fill,
so the respa branch consumed uninitialized grij/expm2/derfc values:
tabulated Coulomb forces and energies were garbage when the PS solver
pair style (respa_enable=1) ran under run_style respa with table
lookups enabled (the default ncoultablebits 12).

Evaluate the ESP force/energy polynomial factors once in the dispatch
(deduplicating the two existing inline copies) and add the espflag
cases to the respa fill and the outer switching region, using the same
full-minus-short-range split (fesp - 1.0) as the style's analytic
compute_outer() path.

Coverity CIDs 1662249, 1661639, 1662021.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
…rature cutoff

The heat-transfer increments dQc/dQd/dQr are computed only when
r < cutT, but the Newton update Q[j] -= (dQc - dQd + dQr) ran for
every pair inside the force cutoff. Whenever the temperature cutoff
is smaller than the force cutoff, neighbor atoms accumulated
uninitialized garbage heat flux, corrupting the eDPD temperature
evolution. Initialize the increments to zero so pairs beyond the
temperature cutoff transfer no heat, consistent with the Q[i] update.

Coverity CIDs 1661734, 1662410, 1662587.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
The random heat-flux term drew a fresh gaussian into randnumT but the
following clamp statement clamped randnum (the force random number)
instead, discarding the fresh draw. The random heat flux dQr thus
reused the same random number as the dissipative force term instead
of an independent one, spoiling the fluctuation-dissipation relation
for the temperature evolution. Found by inspection while fixing the
adjacent heat-flux bug; changes trajectories of eDPD runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
…ld/tip4p

The variable-field branch of post_force() tallied virial contributions
at the M, H1, and H2 sites from the unwrap[] buffer without ever
calling domain->unmap(), so it used stale coordinates left over from
another atom (or garbage), producing a wrong global virial whenever
the field is defined via variables. Add the same unmap calls the
constant-field branch uses.

The atom-style energy variable was also summed as efield[0][3] (the
value of atom 0) at all four tally sites instead of the per-atom
values; index with the site atom like fix efield does.

Coverity CID 1661598.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
The per-(i,j) energy/virial tally after the sub-particle loops passed
fpair/delx/dely/delz values belonging to the last sub-particle pair
inside the sub cutoff, or stale/uninitialized data when all
sub-particle pairs were skipped, producing a garbage (potentially NaN)
pairwise virial contribution for per-atom stress requests. The
sub-particle forces are applied at the atom centers and the global
virial comes from fdotr, so zero the pairwise force/displacement for
discretized interactions and tally only the accumulated energy.

Coverity CID 1661075 (pair line/lj); pair tri/lj shares the pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
When a pair was outside the Coulomb cutoff (reachable whenever the LJ
cutoff is larger) or at near-zero distance, only forcecoul was zeroed
while efield_i kept the value from a previously processed neighbor, so
a garbage electric field contribution was accumulated. Zero both, as
all sibling dielectric pair styles do.

Also apply the same rsq > EPSILON guard to the Coulomb energy block
that the force block uses, so coincident atoms cannot evaluate the
energy from stale prefactor/egamma/table values.

Coverity CIDs 1662310, 1662191, 1661019.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
…es in fix gemc

The random insertion point for an exchange move is sampled on rank 0
(as fractional coordinates for triclinic boxes) and only the Cartesian
coordinates are broadcast, but the owning-subdomain test read lamda[]
on all ranks. Every rank except 0 tested uninitialized stack values,
so the wrong rank (or no rank, or several) could claim the inserted
atom, losing or duplicating atoms in triclinic GEMC runs. Recompute
the fractional coordinates from the broadcast position on all ranks,
as fix gcmc does.

Coverity CID 1662351.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
In FixRigidSmall::resample_momenta() (used by fix hmc) the body-frame
angular velocity wbody is drawn only for bodies in the fix group, but
the rotation into b->omega ran for every body, overwriting the angular
velocity of non-group bodies with values rotated from uninitialized or
stale wbody data. Move the rotation inside the group test.

In the momentum-zeroing loop the group test dereferenced b before it
was assigned to the current body, so the previous iteration's (or the
first loop's last) body decided whether the current body's velocity
was corrected. Assign b first.

Coverity CID 1662345.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
…uck6d

For 1-3 pairs beyond the LJ cutoff (a routine case) only forcebuck6d
was zeroed, and fpair = forcebuck6d * r32inv multiplied zero by an
uninitialized or stale value. If the stale bit pattern happens to be
Inf or NaN the product is NaN and silently poisons the forces. Zero
r32inv together with forcebuck6d.

Coverity CID 1662641.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
The hi-argument parsing block of the region cone constructor had three
defects its correct lo-argument twin does not:

- histyle was never initialized to CONSTANT (only set for v_ input),
  so shape_update() of a variable-shape cone with a constant hi bound
  tested an uninitialized member and could evaluate a garbage variable
  reference (Coverity CID 1661727).
- The EDGE test checked arg[7] (the lo argument), so 'lo EDGE' with a
  numeric hi silently replaced the given hi value with the box edge.
- The y-axis branch was missing its else, so 'hi INF' on a y-axis cone
  was immediately overwritten with the upper box boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
…nders

In surface_exterior() of region cylinder with an open side face
(e.g. 'open 3'), a particle outside the radius closest to a cap was
assigned the cap coordinate along the axis, but the two in-plane
contact-point coordinates were only set for r < radius; with the side
face open they remained uninitialized (or stale), so granular wall
forces from fix wall/gran/region acted toward a garbage point. Project
the contact point onto the cap rim in that case, in all three axis
orientations. For closed cylinders this reproduces the values the
curved-surface block already computed, so behavior there is unchanged.

Coverity CIDs 1662234, 1662893, 1661299.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
The mean squared separation was always averaged over 6 entries of the
distance array even when an atom has fewer than 6 neighbors inside the
cutoff, reading stale data from previous atoms or past the end of the
allocation when no atom in the system has 6 neighbors. Atoms with
fewer than 6 neighbors (free surfaces, small clusters, gas phase) got
a garbage, run-to-run varying structure classification. Average over
min(6, n) neighbors instead, in both the base and the OpenMP version,
which classifies undercoordinated atoms deterministically (typically
as UNKNOWN).

Coverity CIDs 1661953, 2108 (OpenMP); the base class shared the flaw.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
- compute pressure/bocs: p_basis_type, N_basis, N_mol, and vavg were
  only assigned when fix bocs pushes its coarse-grained correction via
  send_cg_info(). Used without fix bocs, compute_scalar() compared the
  uninitialized basis type against BASIS_ANALYTIC (== 0, a common
  value for uninitialized memory) and could dereference the null
  phi_coeff or apply a garbage pressure correction. Initialize the
  basis type to -1 so no correction is applied until one is provided.
  (Coverity CID 1662610)

- fix bond/react: a rate_limit keyword with a numeric Nlimit left
  RateLimit::var_flag (and var_id) uninitialized, so the rate-limit
  check could treat garbage as an equal-style variable reference and
  evaluate a garbage variable index. Use default member initializers.
  (Coverity CID 1661322)

- fix controller: err/sumerr/deltaerr/olderr were uninitialized until
  the first end_of_step(), but compute_vector() can be invoked before
  that (e.g. thermo output at setup), reporting garbage PID terms.
  (Coverity CID 1662773)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
…tarts

The limit_damping flag parsed by pair styles gran/hooke,
gran/hooke/history, and gran/hertz/history was not written to or read
from the restart file, and settings() is not called when reading a
restart, so after read_restart the flag was uninitialized: the damping
limitation could silently turn itself on or off. Append it to the
shared restart settings I/O of the base class.

Coverity CID 1661997.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
Of the six pair_style dsmc arguments only max_cell_size and seed were
stored in the restart file: weighting, T_ref (and the derived kT_ref),
recompute_vsigmamax_stride, and vsigmamax_samples were uninitialized
after read_restart, giving garbage collision statistics. Save and
restore all of them and re-derive kT_ref.

Coverity CID 1661563.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
…estarts

The seqdepflag chosen with the seqav/seqdep pair_coeff argument of
pair styles oxdna/hbond, oxdna/stk, and oxrna2/stk (and their oxdna2/
oxrna2 variants derived from them) was not stored in the restart file,
but init_one() uses it to decide whether the sequence-dependence
scaling of epsilon and shift is applied when symmetrizing the type
pairs. After read_restart an uninitialized flag could silently switch
between sequence-averaged and sequence-dependent interactions.

Coverity CIDs 1661087, 1661247; pair oxdna/stk shared the flaw.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
Only the global cutoff was stored in the restart file. The
limit/eradius and pressure/evirials flags, the per-type ECP element
assignments, and the Pauli core parameters (including per-type values
customized via pair_coeff) were all uninitialized after read_restart,
and the unit-conversion constants h2e/hhmss2e (flagged by Coverity)
were garbage in every force evaluation. Serialize the settings state
and re-derive the unit constants the same way settings() does.

Coverity CID 1662077.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
- next command: listing the same index/loop variable twice made the
  first increment exhaust and remove it, so the second lookup returned
  -1 and variables[-1] was incremented (out-of-bounds write). Reject
  duplicate variable names with a clear error. (Coverity CID 1661328)

- variable formulas: a py_-prefixed function name that does not match
  any variable passed find() == -1 straight into variables[pyvar],
  an out-of-bounds read, instead of the intended error message.
  (Coverity CID 1661224)

- fix store/state: an atom-style variable value dereferenced the
  per-atom storage array unconditionally, which is a null pointer on
  procs without atoms; fix ave/atom already guards the same call.
  (Coverity CID 1662474)

- fix ave/grid: a bracketed atom-style variable reference (v_name[2])
  was accepted but has no array to read, so accumulation dereferenced
  an uninitialized array pointer; reject it at parse time like other
  averaging styles do. Also correct three error messages that referred
  to fix ave/atom. (Coverity CID 1661185)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
…d_order

Both commands document a default output file name (dynmat.dyn and
third_order.dat), but the file was only opened when the file keyword
was given: without it third_order crashed on rank 0 calling ferror()
on a null FILE pointer after every written block, and dynamical_matrix
silently discarded the entire matrix. Open the file unconditionally
and align third_order's default name with its documentation
('Third Order.dat' -> 'third_order.dat').

Coverity CID 1661320.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
pair agni: parameter tags (eta, Rc, n_train, ...) appearing before the
n_elements and interaction headers dereferenced a null params array
with an uninitialized section index; reject such malformed files with
a clean error. (Coverity CID 1662437)

pair rann: none of the comment/blank-line skip loops checked the fgets
return value. Since fgets leaves the buffer unchanged at EOF, an empty
or comment-only potential file - or a valid one with trailing blank
lines - spun forever re-reading EOF. Error out (or fall through to the
existing EOF handling) when the file ends. (Coverity CID 1662231)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
MLIAPData: natoms was narrowed from bigint to int before the
too-many-atoms check, so the check compared already-truncated 32-bit
arithmetic against MAXSMALLINT and could never trigger; the actual
overflow then silently corrupted the global array dimensions. Do the
check on atom->natoms in bigint arithmetic first. (CIDs 1661505, 1662169)

NPair::coord2bin_big: the bin index was computed with 32-bit
multiplications and only widened on assignment, defeating the purpose
of the bigint variant that exists precisely for bin counts beyond
2^31 (hash bins for multi-collection neighbor lists). (CIDs 1662186,
1661428)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
- MyPoolChunk: on invalid constructor parameters the early return left
  npage and the page/freelist pointers uninitialized, so destroying
  the partially constructed pool freed garbage pointers. Initialize
  all destructor-relevant members first and validate the parameters
  before any allocation. (Coverity CIDs 1661308, 1661903)

- fix halt: the MPI_Request array allocated for universe-wide halt
  messaging was never freed. (Coverity CID 1661334)

- fix vector: the nmax keyword accepted values beyond 2^31; the
  storage grow() then truncated while the bigint ring index did not,
  an eventual out-of-bounds write. Reject such values. (CID 1662309)

- fix imd: the protocol-v3 message length is computed in 32-bit as up
  to 36 bytes per atom, overflowing for more than ~59M atoms despite
  the existing atom-count check; tighten that check so the message
  length always fits. (Coverity CID 1662882)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
- compute saed/xrd and fix reaxff/bonds/kk reported memory usage from
  counters that are only set during the first compute or end_of_step,
  printing garbage sizes at setup. (CIDs 1661547, 1661687, 1663254)

- fix surface/local memory_usage overwrote instead of accumulating the
  connectivity terms (missing +=). (Coverity CID 1660985)

- pair runner zeroed 9 doubles into the 6-element Pair::virial array,
  clobbering the 24 bytes of adjacent class members on every global
  virial evaluation, and left the directory pointer uninitialized in
  the constructor although the destructor deletes it. (CIDs 1661855,
  1661672, 1662613)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
- mliap/so3/kk computed allocation sizes and the per-pair data offset
  with 32-bit multiplications before widening to bigint, giving wrong
  (wrapped) allocation sizes and device indices for large systems; the
  CPU version already computes these in 64-bit. (Coverity CIDs
  1663226, 1663089, 1663395, 1663238)

- compute temp/deform/kk declared h_ratelo as Few<double,6> but
  initializes it from the 3-element domain->h_ratelo array, a 24-byte
  overread on every bias removal. (Coverity CID 1663243)

- pair exp6/rx/kk passed errorFlag by value into the vectorized
  mixing-weight path, so the too-few-molecules abort conditions were
  silently swallowed on host builds while device builds abort.
  (Coverity CID 1663155)

- fix wall/gran/old with the DMT normal model read the cohesion energy
  from an uninitialized local that only the JKR branch set, giving a
  garbage friction limit Fncrit. (Coverity CID 1663148)

- fix wall/region/kk silently did nothing (zero forces, undefined
  wall energy and virial) when the region style has no KOKKOS variant;
  now init() reports a clear error. (Coverity CID 1663218)

- pair dpd/fdt/energy/kk instantiated the no-splitting kernels with
  STACKPARAMS=false in both branches of the ntypes dispatch, so the
  stack-parameter fast path was dead code; results are unaffected.
  (Coverity CID 1663182)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
…romptly

pair multi/lucy/rx divided by the total molecule count of a CG
particle without the 10*DBL_EPSILON sanity check that pair table/rx
and pair exp6/rx perform, silently producing NaN weights; add the same
check on the CPU and (via Kokkos::abort) in the KOKKOS version, and
restore it in pair table/rx/kk which defined MY_EPSILON but never used
it after the port. (Coverity CIDs 1661036, 1662158, 1663121, 1663023,
1663371, 1663376)

The destroy_kokkos()/destroy_3d/destroy_4d helpers took the view by
value, so resetting it released only the local copy and the caller's
view kept the allocation alive until reassignment; pass by reference
so device memory is released when requested. (Coverity CIDs 1663205,
1663073, 1663169, 1663306, 1663189)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
… batch

Mechanical follow-up to the batch merged via PR #5058, covering newer
files and members the batch engine missed: cached per-atom array
pointers in atom_vec_apip; the type2rhor/type2z2r maps of pair
eam/apip; the neighbor list pointer of compute ave/sphere/atom; the
per-type coefficient arrays of pair lj/pirani; pair granular's
extra_svector count; pair hdnnp's settings-stage scalars and directory
string (whose garbage value was never freed but also never safely
testable); the per-thread centroid-stress accumulators of ThrData
(also nulled per step in init_force like their eatom/vatom siblings);
the paux1/paux2 pointers of the vendored PTM Voronoi cell; the
matvecs_s/matvecs_t counters of fix qeq/reaxff and qtpie/reaxff; the
cached box/domain pointers and box_drop/box_other/box_touch/point_drop
function pointers of comm_tiled (via init_pointers, covering the
Comm-copy constructor); the write_choice (and dump_atom
convert_choice) function pointers of the atom/cfg/custom/xyz dumps;
the overlap-list counters of Grid2d/Grid3d in both constructors; the
linemin function pointer of min_linesearch; and the multi stencil
pointers of NPair.

Also value-initialize Neighbor::init_pair()'s neigh_bin/neigh_stencil
arrays so the existing defensive null checks compare real null
pointers instead of uninitialized memory.

Coverity CIDs 1662103, 1662812, 1661101, 1661000, 1662461, 1661489,
1661005, 1662718, 1661900, 1661288, 1661621, 1662871, 1661301,
1662252, 1661566, 1661753, 1662305, 1662554, 1662232, 1661906,
1662444, 1661856, 1661718, 1662007.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
Counts and string lengths read from binary restart files were used
directly for allocations and loop bounds; a corrupted or truncated
file produced allocation failures, heap overflows (the LEPTON styles
read per-entry expression lengths into a buffer sized by an unchecked
maxlen), out-of-bounds writes (pair granular nmodels vs maxmodels), or
stalled unpack loops (read_restart per-atom chunks). Add range checks
with clear 'restart file' error messages right after each read: the
hybrid styles' nstyles and keyword lengths, LEPTON expression buffer
and entry lengths, the gaussian/fourier/nharmonic/spherical term
counts, granular model names, pair tracker fix ID and value counts,
group names, Modify's global/per-atom restart counts (and the buffer
size accumulation, now done in 64-bit), the native dump reader's
label-line length and chunk atom count, and the per-atom section of
read_restart itself. Guards on broadcast data are placed after the
broadcast so all ranks take the error path collectively.

fix bond/react map files: template atom IDs were checked against the
upper bound only, so zero or negative IDs in a hand-edited map file
wrote out of bounds; add the lower bounds, a missing sscanf return
check, and a range check on the constraint count.

Coverity CIDs 1661508, 1661577, 1661546, 1662928, 1662591, 1661650,
1663159, 1661659, 1661596, 1662805, 1662556, 1660975, 1661125,
1661707, 1661116, 1662541, 1663011, 1663348, 1663285, 1663253,
1663022, 1661793, 1662734, 1661685, 1661527, 1661593, 1661079,
1662143, 1661625, 1661169, 1662250, 1662573, 1662683, 1661056,
1661054, 1661570, 1662274.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNvuaNpJNkbCKaAE8ngPQv
akohlmey and others added 27 commits July 9, 2026 15:21
26 scenes: acolor with wildcard color cycling, custom color definitions
(RGB triplet and hex), adiam type ranges, color maps in all four styles
(ca/cf/da/sf) on the atom charge plus a custom bond value map (bmap),
per-type bond colors and diameters, atom transparency as a water shell
around the peptide with and without FSAA, mixed-transparency bonds
(regression test for the half-bond transparency fix), bond transparency
(btrans), background gradient (backcolor2), light settings, up vector,
FSAA-only, box/axes/subbox line transparency, ellipsoid rendering as
triangle mesh and wireframe, body particle rendering (faces, wireframe,
both), and region plane/sphere/open-face-block drawing.

Notes added to the README: btrans only applies when bonds are not
colored by atom (with 'bond atom' the halves follow atrans); continuous
color maps require literal min/max first/last entries and discrete maps
a final catch-all bin.
When bonds are colored by atom, each bond is drawn as two half
cylinders so that each half can follow the color of its atom.  The
second half was drawn with the color of its atom but the transparency
(dump_modify atrans) of the *other* atom, so the two halves of a bond
between an opaque and a transparent atom were rendered inconsistently.
Apply color and transparency from the same atom, as the autobond
drawing code already does.
8 scenes: the frame, filled (with open faces), transparent, and points
draw styles across six region geometries (block, sphere, ellipsoid,
cylinder, cone, prism), union and intersect regions drawn via the
points style, and dynamic regions - the move and rotate keywords driven
by equal-style variables and variable-driven radii - rendered after a
100-step run so that the compared image shows the displaced, rotated,
or grown region (which also exercises comparing the last frame of a
multi-frame dump).
The test_min_style tester sets up the fourmol molecular test system like
the fix-timestep tester, applies min_style, min_modify, and optional fix
commands from the post_commands block of the test YAML file, and runs a
minimization with a fixed iteration budget (100 iterations, tolerances
zero) so that the reference data is deterministic.  It compares the
potential energy before and after the minimization, the total force norm,
all six box dimensions and tilt factors (so changes from fix box/relax
are captured and an unchanged box is verified everywhere else), and the
per-atom positions.  It also checks that the minimization lowered the
energy.  The tester doubles as the reference generator (-u/-g).

12 tests: cg with the quadratic (default), backtrack, and forcezero line
searches and a dmax setting, sd, hftn, quickmin, fire with default
settings, verlet integrator, and in abcfire mode, and fix box/relax in
iso and aniso mode with min_style cg.
The test_output_style tester runs a test system (the fourmol molecular
system or the in.metal template) for 10 MD steps and then collects and
compares all output data provided by the compute or fix with the ID
'test' defined in the post_commands block of the test YAML file.  It
introspects the style's output flags and handles global scalars and
vectors (existing YAML keys), global arrays including variable-size
per-chunk arrays, per-atom vectors and arrays, and local vectors and
arrays (three new additive keys in the shared test configuration:
global_array, peratom_data with the atom tag as first column, and
local_data).  The driver adds its own nve integrator - unless the test
defines a time-integrating fix - so that history-dependent styles (msd,
vacf, ave/time, ave/atom) report real data.  The tester doubles as the
reference generator (-u/-g).

23 initial tests covering all output categories: gyration, com, dipole,
msd, vacf, reduce, slice, temp/profile, rdf, com/chunk, coord/atom,
ke/atom, displace/atom, property/atom, property/local, bond/local,
angle/local, pair/local, cna/atom, centro/atom, orientorder/atom, and
the fixes ave/time and ave/atom through the same driver.

Not yet supported: computes requiring per-atom energy or virial tallies
during the run (pe/atom, stress/atom); these need consumption during
the run or additional driver support.
Broad sweep over compute styles using the existing test systems:
- the chunk family (chunk/atom, gyration/angmom/torque/vcm/omega/inertia/
  dipole/msd/temp/property/reduce/com chunk computes, chunk/spread/atom,
  global/atom) using per-molecule chunks
- temperature/pressure/energy variants (temp, temp/com, temp/partial,
  temp/deform, temp/ramp, temp/region, ke, pe, pressure, pair,
  group/group, momentum, msd/nongauss, gyration/shape, viscosity/cos)
- connectivity (cluster/atom, fragment/atom, aggregate/atom) and bonded
  data (dihedral/local, improper/local, hbond/local)
- profile computes (adf, stress/mop, stress/mop/profile, stress/cartesian)
- the TALLY package computes (pe, pe/mol, force, stress, heat/flux)
  whose callbacks accumulate during the run
- rigid body computes (rigid/local, erotate/rigid, ke/rigid via fix
  rigid/small, using the driver's integrator detection)
- lattice analysis on the metal system (ackland, basal, entropy,
  hexorder, sna/atom, voronoi) and diffraction (xrd, saed)
- aspherical particle computes (temp/asphere, erotate/asphere) on the
  ellipsoid system
- reporting fixes (ave/chunk, ave/histo, ave/histo/weight, ave/correlate,
  numdiff, numdiff/virial)

The driver now also supports variable-size global arrays for fixes
(e.g. fix ave/chunk).  Dropped as unsuitable for the current driver:
compute angle/bond/dihedral/improper (require hybrid bonded styles).
restore_lighting() called std::clamp(0.0, 1.0, value) instead of
std::clamp(value, 0.0, 1.0).  Since the 'high' bound is then smaller
than the 'low' bound, the behavior is undefined by the C++ standard and
actually differs between standard library implementations: with GNU
libstdc++ the swapped call happens to return the intended value, but
with libc++ (macOS) and MSVC (Windows) all light intensities were set
to 1.0 whenever dump_modify lights or the internal lighting
save/restore around flat-surface drawing was used, producing visibly
different renderings.  Found by the dump-modify-lights unit test
failing on the macOS and Windows CI while passing on Linux.  Reference
data is unaffected since it was generated on Linux where the swapped
call produced the intended values.
restore_lighting() called std::clamp(0.0, 1.0, value) instead of
std::clamp(value, 0.0, 1.0).  Since the 'high' bound is then smaller
than the 'low' bound, the behavior is undefined by the C++ standard and
actually differs between standard library implementations: with GNU
libstdc++ the swapped call happens to return the intended value, but
with libc++ (macOS) and MSVC (Windows) all light intensities were set
to 1.0 whenever dump_modify lights or the internal lighting
save/restore around flat-surface drawing was used, producing visibly
different renderings.  Found by the dump-modify-lights unit test
failing on the macOS and Windows CI while passing on Linux.  Reference
data is unaffected since it was generated on Linux where the swapped
call produced the intended values.
Replace the std::stringstream, sscanf(), atof(), and atoi() based
parsing in TestConfigReader with the Tokenizer and ValueTokenizer
classes and utils::split_words(), and the stringstream in
TestConfig::tags_line() with plain string concatenation. The
repeated per-atom and scalar parsing code is consolidated into
shared helper functions. Unlike sscanf(), malformed fields now
abort with a clear message instead of silently producing garbage
reference data, and atom tags are checked against the natoms range.

Verified: full force-styles test suite passes and regenerating a
yaml file with TEST_ARGS=-u remains idempotent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZmFEmpx4SmV2vaUWBF4Yk
Convert the parsing of the global_array, peratom_data, and local_data
reference blocks added for the output-style tester from stringstreams
to the Tokenizer/ValueTokenizer idiom of the preceding refactor, using
a static helper function like the other block parsers.  Malformed
fields now abort with a clear message like everywhere else.

Verified: all 487 force-styles tests pass and regenerating reference
data remains idempotent.
The Windows CI runs for the minimizer tests showed that the state after
a fixed-iteration minimization is not reproducible across compilers and
math libraries: the line search logic branches on floating-point
comparisons, so last-bit force differences send the descent along a
different but equally valid path.  All paths still land in the same
basin, so the final potential energies agree to about 1e-4 while the
per-atom positions and the force norm can differ by large amounts
(up to 92% for hftn positions, 91% for the quickmin force norm).

Thus compare only observables that are stable against such path
changes: keep the energy and box dimension checks with the per-file
epsilon loosened to 1e-3 (10x the largest observed cross-platform
drift, still well below the differences between minimizer styles),
turn the force norm check into an upper bound (10x the reference), and
drop the per-atom position references entirely.  Corrupted-reference
checks confirm that broken minimizers are still detected.
Add the global_array, peratom_data, and local_data reference keys and
oneOf branches for the test_min_style and test_output_style programs.
Also catch up with previously missing keys: timestep and the magnetic
reference data of the SPIN tests (init_mag_forces, run_mag_forces,
run_spin), and no longer require input_coeffs for test_fix_timestep
(the ILVES references use a self-contained input template).  All 714
YAML reference files now validate.
Describe the two new test programs in the force-styles section of
Developer_unittest.rst: what they set up, what they compare and why
(including the portability considerations of the minimizer tests), and
how reference files are generated.  Update the configuration key table
with the new and newly shared keys, fix the copy-and-paste error in
the manybody pair style test category, and note that the granular
tests require the GRANULAR package.
Add a section on the unittest/graphics test suite to
Developer_unittest.rst: how scenes are described by the YAML files, how
the sampled pixel data (row/column mean RGB projections and pixel block
samples) is stored in the same file and compared, why the reference
data depends on the MPI rank count, and how to author and generate new
tests.  Two figures rendered from an actual test scene illustrate the
sampling scheme and how a rendering change is detected.
Coverity Scan triage part 1: bug fixes
Coverity Scan triage part 2: defensive hardening
Coverity Scan triage part 3: regression tests
Add force-styles testers for minimizer styles and compute/fix output data
Add YAML-based test facility for dump image and GRAPHICS package rendering
Overhaul regression test reporting and add result merging tool
@pull pull Bot locked and limited conversation to collaborators Jul 11, 2026
@pull pull Bot added the ⤵️ pull label Jul 11, 2026
@pull
pull Bot merged commit 7b8a425 into comphy-lab:develop Jul 11, 2026
4 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant