Fix BEC NaN handling for structures without target data in read_structures

Problem

read_structures is supposed to replace zeros with NaN for Born effective charge (BEC) data when a structure has no BEC targets — GPUMD writes zeros in bec_*.out as a placeholder in that case. The detection code was broken in three ways:

  1. Wrong dict: the condition checked s.info['bec_target'], but BEC is now per-atom data stored via structure.new_array(...) in s.arrays['bec_target'] (!220 (merged)). The condition never fired, so placeholder zeros were silently passed through as real data.
  2. Fragile heuristic: even if the dict were correct, np.allclose(..., 0) would misidentify a structure whose BEC values happen to be all zero. The reliable signal is 'bec' not in s.arrays — the presence of a bec array in the source xyz file is what indicates that target BEC data was actually provided.
  3. Incomplete replacement: only bec_target was being NaN-ed; bec_predicted (also zeros for untargeted structures) was left as-is.

The same three issues affected the tests: the setup used structure.info to carry the BEC signal, and the assertions looked in s_read.info rather than s_read.arrays.

Changes

calorine/nep/io.py — replace the NaN-replacement block with a corrected version that checks s.arrays, uses 'bec' not in s.arrays for detection, and NaN-s both bec_target and bec_predicted.

tests/test_nep_io.py — two fixes in set_up_output_for_testing and test_read_structures_potential:

  • Set structure.arrays['bec'] = becs (instead of only data['bec_target'] = becs) for structures that have BEC target data, so the detection signal is present in the written xyz.
  • Update the assertion loop to skip bec_target/bec_predicted in the s.info pass and check them separately via s_read.arrays, branching on 'bec' not in s_ref.arrays to verify NaN vs. real values.

Merge request reports

Loading