-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Q2d_nm_c_to_a_b needs a non-zero positive and negative m to work #87
Comments
Thanks for the bug report. A mild tangent to the bug -- the way prysm computes all of its polynomials is derived from the teachings of Greg Forbes, by using recurrence relations. That is in contrast to how, say, POPPY computes Zernikes using Rodrigues' form. The recurrence relations are generally straightforward to differentiate, and I wanted to make a raytracer at the end of 2021, so there be derivatives. Forbes is truly a genius, and buried in the appendix of his papers is a method for computing not only the surface sag, but also its derivatives, in a single pass without ever computing either the polynomials or their derivatives directly. I actually asked him about computing the derivatives directly over e-mail in 2018 or 2019, and he said he never bothered because for raytracing or surface fitting, you don't actually ever have to do that. Since I am very bad at math and the creator never wrote down how to do do it, there is not a If you pass a single n/m to compute_z_zprime, it will fail because it needs all of the coefficients from n=0..N, even if the lower several are zero. This is a contrivance of the clever math Forbes used, and I typed into the computer. The reason your code is crashing is perhaps hidden in the docstring of
When you use range(1,3) you are doing Z1 (piston) and Z2 (tip). This will lead to cm being a length 1 list, ams would be a length 1 list, and bms a length 0 list. The Q2D routines could be modified to remove this constraint, but it seems to only manifest in teaching contexts. Most real usages will expand as high as, say, 3 terms in each c, a, and b. |
Right there with you with you only struggling through these maths heavy optics papers, really thinking I should have paid more attention in my vector calc classes... Interesting to hear that he never bothered to write the derivatives on their own, based especially based on other papers for orthogonal zernike derivatives alone being published (eg Vector polynomials orthogonal to the gradient of Zernike polynomial, A. Gavrielides, 1982 or Determination of phase mode components in terms of local But I guess I'll give him some slack given the combined wavefront and orthogonal derivatives computation, given reconstructing a wavefront is generally what derivatives are intended to be used for(?) And of course for coming up with such a useful set :) Agreed on the compute_z_zprime_Q2d restriction - there's no top level prysm wrapper that puts the output of Q2d_nm_c_to_a_b straight into compute_z_zprime_Q2d, so the first returning invalid data for the latter is the user's problem not prysm's. This is probably my fault for starting to write out something for that case however before changing my mind. But the part which actually seems like a bug is
(and likewise if you only have negative m it will error in the same way on |
I think the predominant use for derivatives in optics is for raytracing, where only the surface sag and its derivatives matter, and the individual basis functions have no intrinsic meaning on their own. It is true that there are a number of wavefront sensors (ok, maybe half or so of them) that fundamentally sense gradients -- shack hartmanns, modified hartmann masks ("quadriwave lateral shearing interferometer"), pyramid WFS, etc. But there exist zonal reconstruction algorithms for those sensors, which AFAIK are the predominant methods used. Anyway.... the "integration" of derivatives in prysm is fairly poor. I wrote all of that code as part of the
For example,
If you want to extract the derivative w.r.t. a specific term, just set all of the elements of cms/ams/bms to zero except that one element. For example, to do this for the second-order astigmatism,
Technically bms could be Realistically, the I guess it could still be removed, since prysm is <= v1.0... This is certainly an area where pull requests (especially to enhance docs!) would be appreciated :) |
I've gone and reread the original forbes paper and see what you mean now my brain has engaged a bit more. I guess given the matrix form there's no sensible way to get to a monomial indexing at all? Each could just extend off forever, besides the "m + 2n ≤ T" relations described just above eq 2.3. It might take me a while, but I'll see if I can come up with a bit of example code that uses a set of modes from that basis to say fit a random wavefront? Specifically using the nomenclature in the paper where its just the A term's matrix that is used. Could be a nice little learning exercise (for both myself and anyone coming across the lib) then maybe I'll be able to contribue something more useful to the code/api docs themself. |
The matrix is just a dense way of representing The reason the code uses this matrix to represent the coefficients is firstly just because that's how Forbes presented it in the paper, which is pretty trite. But the "core" reason is that the mathematical formalism Forbes teaches to compute orthogonal polynomials in general, but especially for Qs, starts from, say, m=0,n=0, and uses that along the way to computing m=0,n=1, then uses that to compute m=0,n=2, and so on. It would be completely equivalent to make a Zernike interface in the same formalism, in fact Forbes does have a paper on fitting Q polynomials. It uses yet more exotic mathematics and not least squares, and I have not implemented it (since you can just use least squares). I don't have any knowledge of fitting based on gradient measurements -- you might send an email to Forbes about that; he probably would be interested in writing a paper on the topic. |
So I've been trying to wrap my head around all things Q2d, and so far compute_z_z_prime seems to the be function I need. Right now the Q2d function will take any n and m values in terms of zernike-esque indices. However trying to do that with a combination of
Q2d_nm_c_to_a_b
andcompute_z_zprime_Q2d
you run into errors if negative and positive m terms don't match.(Ignore the variable name and fact this is based off of zernike code for a sec)
In this example if I do
zernikes = np.arange(1, 3)
, which corresponds to the following nms in zernike indexing:(n: 0, m: 0) (n: 1, m: 1)
It will error in
Q2d_nm_c_to_a_b
with:Now this obviously isn't a very imposing issue to work around, but it is one I had to peak at the source of qpoly.py to figure out, and the docstrings don't list the restriction.
So it looks like either a case the function should handle or have listed as a restriction?
The text was updated successfully, but these errors were encountered: