Skip to content

Sort spectral lines by their nominal intensity, and draw uncertain spectra at it - #12

Merged
roytsmart merged 2 commits into
mainfrom
fix/nominal-line-order
Oct 6, 2026
Merged

roytsmart merged 2 commits into
mainfrom
fix/nominal-line-order

Conversation

@roytsmart

@roytsmart roytsmart commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

utu.spectrum.lines() puts the brightest line first with np.argsort(result.outputs, axis="line"). Today named-arrays sorts an uncertain array by the mean of each element, so this works for an uncertain intensity. sun-data/named-arrays#252 makes np.argsort sort the nominal value and each sample separately. Once utu picks up that release, an intensity whose uncertainty differs between temperatures (an uncertain DEM, for example) would put the lines in a different order in each sample. The line axis would then no longer identify a line, and the wavelength and ion labels would become uncertain arrays.

For example, with O V and Mg X between 600 and 640 Å and a ±90% emission measure that is independent in each temperature bin, the ion labels came back as an UncertainScalarArray, and result.inputs.ion[{"line": 0}].ndarray raised AttributeError.

Changes

  • lines() sorts by na.nominal(result.outputs). Every line keeps one place in every sample, its wavelength and ion stay certain, and its intensity keeps its full uncertainty. An individual sample may therefore not be brightest-first; the docstring says so and why. For a certain intensity nothing changes.
  • stem() draws the whole spectrum, wavelengths included, at its nominal value, which also picks the brightest lines to label by their nominal intensity. Before this it couldn't draw an uncertain spectrum at all, with any named-arrays version, because it reads the intensity with .ndarray.
  • stem() skips lines whose nominal intensity isn't finite. Under #252, selecting lines with an uncertain threshold (s[s.outputs > t]) keeps every line any sample selected, and a line the nominal value didn't select is NaN in the nominal spectrum. That line isn't in the nominal spectrum, so it isn't drawn. Before, it crashed the axis limits ("Axis limits cannot be NaN or Inf").

Tests

  • test_lines_uncertain makes one lines() call with an uncertain emission measure whose first sample is the nominal value and whose other 19 scale each temperature by a seeded random factor. It checks that:
    • the wavelengths and ions are plain arrays;
    • the nominal intensity is brightest-first;
    • the first sample equals the nominal, so every sample moved with its line;
    • at least one other sample would have ordered the lines differently, so the test keeps testing the order.
  • test_stem_uncertain draws a spectrum whose brightest line differs between samples. The labels follow the nominal order, and the stems have the nominal heights.
  • test_stem_uncertain_unselected draws a spectrum shaped like an uncertain threshold's result, with one line NaN in the nominal: only the other two lines are drawn and labelled.

Against named-arrays#252, each test fails without the change it covers and passes with it. The full suite passes with released named-arrays (84 passed) and with #252 (84 passed). na.nominal has recursed into function arrays since named-arrays 2.8.0, so the named-arrays~=2.8 pin is unchanged.

This should merge before utu moves to the named-arrays release that contains #252.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RYMQjkrAa18mn8SFiR4Bna

`lines()` sorted the lines with `np.argsort` of their intensity. Once
named-arrays sorts uncertain arrays sample by sample (sun-data/named-arrays#252),
an intensity whose uncertainty differs between temperatures would put the
lines in a different order in each sample, which makes the wavelength and
the ion of each line uncertain. The order is now that of the nominal
intensity, so each line stays in one place and its labels stay certain.

`stem()` could not draw an uncertain spectrum at all, since it reads the
intensity with `.ndarray`. It now draws the nominal value, which also
picks the brightest lines to label by their nominal intensity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RYMQjkrAa18mn8SFiR4Bna
@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (d33cb27) to head (264396e).

Additional details and impacted files
@@            Coverage Diff            @@
##              main       #12   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           17        17           
  Lines         1007      1044   +37     
=========================================
+ Hits          1007      1044   +37     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@read-the-docs-community

read-the-docs-community Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

… the order

- `stem()` draws only the lines with a finite nominal intensity. A line
  which only some samples of an uncertain selection kept, like one an
  uncertain threshold let through, is NaN in the nominal spectrum, and
  crashed the axis limits and the labels.
- The docstrings say that `lines()` orders an uncertain spectrum by its
  nominal intensity and why, and that `stem()` draws the whole spectrum,
  wavelengths included, at its nominal value.
- `test_lines_uncertain` calls `lines()` once instead of twice. Its first
  sample is the nominal emission measure, so it checks that every sample
  moved with its line, and it checks that some samples would have ordered
  the lines differently, so that it keeps testing the order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RYMQjkrAa18mn8SFiR4Bna
@roytsmart
roytsmart merged commit 2f1880b into main Oct 6, 2026
13 checks passed
@roytsmart
roytsmart deleted the fix/nominal-line-order branch October 6, 2026 23:12
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