Fix dynamic fractional masking formula

This is the result of testing CoPilot CLI (took around 3% of premium requests).

Fix dynamic fractional masking formula (Σ(w) ≠ 1 case)

Closes #168 (closed)

Problem

When dynamic fractional masking is used with explicit interpolation weights, the formula

F_t = \frac{\sum_i F_{s,i} \cdot f_i \cdot w_i}{\sum_i f_i \cdot w_i}

produces incorrect results whenever \sum_i w_i \neq 1. The most visible symptom: when all fractional mask values are 1 (all sources fully valid), the formula reduces to

F_t = \frac{\sum_i F_{s,i} \cdot w_i}{\sum_i w_i}

instead of the expected F_t = \sum_i F_{s,i} \cdot w_i, silently rescaling the interpolation result. This affects:

  • Conservative interpolation at boundary cells — partial source coverage means \sum_i w_i < 1, so the formula inflates the result relative to the no-mask case.
  • Any interpolation method whose weights are not pre-normalised to sum to 1.

Fix

Multiply the result by s = \sum_i w_i:

F_t = \frac{\sum_i F_{s,i} \cdot f_i \cdot w_i}{\sum_i f_i \cdot w_i} \cdot \sum_i w_i

When all f_i = 1: \sum_i f_i w_i = \sum_i w_i = s, so F_t = \sum_i F_{s,i} w_i — identical to the no-mask result for any \sum_i w_i. This formula was already used in tests/mci/test_dummy_coupling_raw_c.c as the reference, which motivated the issue.

The fix applies only to the explicit-weights path (weights != NULL in compute_tgt_field_wgt). The unweighted path (implicit w = 1 per source, used for NNN-style fractional averaging) has different semantics and is left unchanged.

Changes

File Description
src/core/interpolation/interpolation_utils.h Split COMPUTE_TGT_FIELD_WGT_FRAC_ into FRAC_W_ (explicit weights, new formula with weight_sum tracking) and FRAC_NW_ (unweighted, old formula); update dispatcher
tests/core/interpolation/test_interpolation_parallel6.c Update ref_tgt_data_raw[1] entries for tgt 5, 8, 10 (where \sum w_i \neq \sum f_i w_i) in both MVP-at-src and MVP-at-tgt frac test blocks
tests/core/interpolation/test_interp_weights_parallel.c Update apply_interp_weights_data() reference to accumulate weight_sum and apply the corrected formula
doc/src/fractional_mask.dox Update formula to include s = \sum_i w_i; add explanatory note on the all-ones frac case; update worked examples; remove outdated remark that frac masking only makes sense when weights sum to 1
doc/src/version_info.dox Add release note entry under Version 3.15.0

Testing

tests/mci/test_dummy_coupling_raw_c.c already used the correct formula and required no changes — it serves as confirmation that the new behaviour is consistent with the existing raw-exchange reference.

Checklist

  • All code changes are covered by unit tests
  • Code coverage did not deteriorate
  • Code follows Style Guide
  • Documentation updated, if necessary
  • WIP: Manual running of configure/build jobs was successful
  • Update version info, if changes are worth mentioning (Develop section in doc/src/version_info.dox, add section if not yet included)

Merge request reports

Loading