Add optika.zernikes and ZernikeSag#172
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## fix/polygonal-aperture-bounds #172 +/- ##
=================================================================
+ Coverage 99.35% 99.37% +0.02%
=================================================================
Files 116 120 +4
Lines 6024 6248 +224
=================================================================
+ Hits 5985 6209 +224
Misses 39 39
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| plt.legend() | ||
| """ | ||
|
|
||
| sag: None | AbstractSag = None |
There was a problem hiding this comment.
Kinda unfortunate this is called sag as well, if you refer to it, it has stutter: sag.sag. Is there a better word like base or sag_base or something that captures it without using the exact same word?
There was a problem hiding this comment.
Good call — renamed the attribute to base, so it now reads sag.base. Done in 8455e8d.
| plt.figure() | ||
| na.plt.plot(position.y, z, axis="y", label="perturbed") | ||
| na.plt.plot(position.y, z_base, axis="y", label="base") | ||
| plt.legend() |
There was a problem hiding this comment.
I don't see any difference between the two curves in this plot.
There was a problem hiding this comment.
You're right, and it was a real bug in the example: the perturbation is coma (Noll index 8), which varies as cos(phi) and is therefore identically zero along the y axis — exactly the slice the plot used, so the curves landed on top of each other. Switched the slice to vary along x, where the coma term is nonzero (~2.8 mm peak difference). Fixed in 8455e8d.
|
|
||
| def zernike( | ||
| j: int, | ||
| position: na.AbstractCartesian2dVectorArray, |
There was a problem hiding this comment.
I feel like position should be first, is it standard for this function to put j first?
There was a problem hiding this comment.
Agreed — there's no strong convention for j-first, and putting position first matches the other field-evaluation functions. Flipped both zernike() and zernike_gradient() to (position, j) and updated the call sites. Done in 8455e8d.
…adients Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…profile Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Rename the ZernikeSag base-profile attribute from `sag` to `base` to avoid the `sag.sag` stutter when referring to it. - Fix the ZernikeSag docstring example: the coma term (Noll index 8) varies as cos(phi) and is identically zero along the y axis, so the original y-slice showed the perturbed and base curves on top of each other. Slice along x instead, where the perturbation is visible. - Put `position` first in the signatures of `zernike()` and `zernike_gradient()`, matching the convention of the other field-evaluation functions; update call sites and tests accordingly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1759e2b to
9479771
Compare
4f496ec to
8455e8d
Compare
Summary
Stacked on #171 (will retarget to
mainonce it merges; the branches are independent in content).optika.zernikes: Noll-indexed, RMS-normalized Zernike polynomials (noll(),zernike()) and their analytic gradients (zernike_gradient()), matching the convention of :cite:t:Noll1976(reference added torefs.bib). The gradient uses an origin-safe series forR_n^m(rho)/rho, so it is exact at the center of the pupil.optika.sags.ZernikeSag: a sag profile that perturbs a base sag with a sum of Zernike polynomials, for modeling measured or analytic figure error. Elementiofcoefficientsis the Noll-j = i + 1coefficient over the disk of the givenradius. The analyticnormal()combines the base gradient with the Zernike gradients; the ray intercept falls back to the inherited numeric solver.Because the figure error perturbs the surface, the same object is seen consistently by both the geometric raytrace and (in an upcoming PR) the physical-optics propagation - a single source of truth for tolerancing.
Tests
noll()against the published index table; closed forms for j = 1, 2, 3, 4, 11.ZernikeSagthrough the standardAbstractTestAbstractSagbattery, plus a geometric check that Noll Z4 shifts the focus of a paraboloid by the analytic amount.🤖 Generated with Claude Code