Found via a NumPy UserWarning surfaced in #404's CI (unrelated PR - just where it was noticed).
`pyopia.statistics.get_j()`:
```python
p = np.polyfit(
np.log(dias[ind]),
np.log(number_distribution[ind], where=number_distribution[ind] > 0),
1,
)
```
`where=` without a matching `out=` tells NumPy to skip computing `log()` for non-positive values (guarding against `log(0)`/`log(negative)`), but the skipped positions in the result are left as uninitialized memory - whatever bytes happened to already be there - not zero, not NaN. That array is then fed directly into `np.polyfit` for the Junge slope fit.
`number_distribution` is a real particle-count distribution, so a zero-count bin in the 150-300 um fitting range is a completely normal situation (an empty size bin), not an edge case. Whenever that happens, the Junge slope calculation is currently corrupted by garbage at exactly those points, rather than excluding them as the `where=` clause seems to have intended.
`get_j()` is called from `nc_vc_from_stats()`, which returns `junge_slope` as one of its four outputs and ultimately populates the `junge` column in `image_stats` (see `pyopia.process.CalculateImageStats`) - so this affects real, user-facing output, not just an internal detail.
Fix: exclude non-positive `number_distribution` bins from the fit entirely, by folding `number_distribution > 0` into the `ind` mask used for both the x (`log(dias)`) and y (`log(number_distribution)`) arrays, rather than only masking the `log()` call itself.
No existing test coverage for `get_j()`.
Found via a NumPy UserWarning surfaced in #404's CI (unrelated PR - just where it was noticed).
`pyopia.statistics.get_j()`:
```python
p = np.polyfit(
np.log(dias[ind]),
np.log(number_distribution[ind], where=number_distribution[ind] > 0),
1,
)
```
`where=` without a matching `out=` tells NumPy to skip computing `log()` for non-positive values (guarding against `log(0)`/`log(negative)`), but the skipped positions in the result are left as uninitialized memory - whatever bytes happened to already be there - not zero, not NaN. That array is then fed directly into `np.polyfit` for the Junge slope fit.
`number_distribution` is a real particle-count distribution, so a zero-count bin in the 150-300 um fitting range is a completely normal situation (an empty size bin), not an edge case. Whenever that happens, the Junge slope calculation is currently corrupted by garbage at exactly those points, rather than excluding them as the `where=` clause seems to have intended.
`get_j()` is called from `nc_vc_from_stats()`, which returns `junge_slope` as one of its four outputs and ultimately populates the `junge` column in `image_stats` (see `pyopia.process.CalculateImageStats`) - so this affects real, user-facing output, not just an internal detail.
Fix: exclude non-positive `number_distribution` bins from the fit entirely, by folding `number_distribution > 0` into the `ind` mask used for both the x (`log(dias)`) and y (`log(number_distribution)`) arrays, rather than only masking the `log()` call itself.
No existing test coverage for `get_j()`.