Skip to content

Let a plot be drawn in a unit of the caller's choosing - #201

Merged
roytsmart merged 2 commits into
mainfrom
feature/plot-unit
Aug 21, 2026
Merged

Let a plot be drawn in a unit of the caller's choosing#201
roytsmart merged 2 commits into
mainfrom
feature/plot-unit

Conversation

@roytsmart

Copy link
Copy Markdown
Collaborator

A system is free to describe its parts in whichever units suit them, and a
detector measured in microns beside optics measured in millimeters is quite
normal. ESIS is built that way: its sensor comes from msfc-ccd, where a pixel
pitch is naturally microns, while the optics are millimeters.

Nothing reconciles those units on the way to matplotlib.
astropy.visualization.quantity_support does it on a 2D axes, but not on a
3D one
, so a surface given in microns is drawn a thousand times too large:

d = esis.flights.f1.optics.design(num_distribution=0)
ax = plt.figure().add_subplot(111, projection="3d")
d.system.plot(ax=ax, components=("y", "z", "x"), plot_rays=False)
ax.get_zlim()   # (-121491.1, 121491.1)

That is microns being read as millimeters. The instrument collapses to a speck
and the detectors land outside the frame entirely, with nothing to say so. It
is a quiet failure: the geometry is correct, only the scale it is drawn at is
wrong, so a layout figure simply comes out missing its detectors.

The change

plot gains a unit, and converts every length to it before drawing: the
surfaces, both of their apertures, and the rays.

d.system.plot(ax=ax, components=("y", "z", "x"), plot_rays=False, unit=u.mm)
ax.get_zlim()   # (-135.8, 135.8)

The default is unchanged, so nothing that works today moves.

This mirrors _write_to_dxf, which sits directly below plot in each of these
classes and has always taken a unit and converted. The plotting path was the
one that did not.

Why not fix the sensor

The other option was to have the sensor describe itself in millimeters, as the
older kgpy model did, where the detector's clear width was computed as
(width_pixels * shape_pixels).to(u.mm). That would fix ESIS and no one else,
and it puts the burden on how a part is specified rather than on how it is
drawn. Specifying a pixel pitch in microns should stay comfortable.

Testing

test_plot_unit builds the same aperture twice, once in millimeters and once
in microns, and checks the magnitude handed to matplotlib. Without a unit the
two differ by a factor of a thousand; with one they agree; and asking for
microns converts rather than relabels. I checked it fails without the change.

The existing suites pass: 1641 in apertures, 451 in systems/_sequential.

A system is free to describe its parts in whichever units suit them, and
a detector measured in microns beside optics measured in millimeters is
quite normal: ESIS is built that way, its sensor coming from `msfc-ccd`
where a pixel pitch is naturally microns.

Nothing reconciles those units on the way to matplotlib.
`quantity_support` does it on a 2D axes, but not on a 3D one, where a
surface given in microns is drawn a thousand times too large and the
instrument around it collapses to a speck. Plotting the ESIS design in 3D
gives a `zlim` of 121491, being microns read as millimeters, and the
detectors land outside the frame entirely without anything saying so.

`plot` now takes a `unit`, and converts every length to it before
drawing: surfaces, both apertures, and the rays. Passing `u.mm` for that
same system gives a `zlim` of 135.8. This mirrors `_write_to_dxf`, which
has always taken a unit and converted, and leaves the default behaviour
as it was.

The alternative was to make the sensor describe itself in millimeters,
which would fix ESIS and no one else, and would put the burden on how a
part is specified rather than on how it is drawn.

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

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.58%. Comparing base (beadb92) to head (3518101).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #201   +/-   ##
=======================================
  Coverage   99.58%   99.58%           
=======================================
  Files         118      118           
  Lines        6933     6963   +30     
=======================================
+ Hits         6904     6934   +30     
  Misses         29       29           
Flag Coverage Δ
unittests 99.58% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

The aperture test reached the conversion of a surface but never the one
applied to the rays, since nothing called `plot` on a whole system with a
unit.

The system is now drawn twice, in millimeters and in microns, and the
magnitudes handed to matplotlib have to differ by exactly the factor
between the two. That covers the rays, and it checks the thing worth
checking: that everything drawn is converted, not merely some of it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYjDL98znSud1yFh9chnkP
@roytsmart
roytsmart merged commit 462fbfb into main Aug 21, 2026
12 checks passed
@roytsmart
roytsmart deleted the feature/plot-unit branch August 21, 2026 19:37
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