How to create a psf from a pupil with the new (v0.20) style?
See original GitHub issueHello,
This is an incredibly promising package, bravo for all the work! I’ve installed the current development branch (0.19.2.dev129) which I later on discovered is quite different from the stable version: https://prysm.readthedocs.io/en/latest/releases/v0.20.html
This explains why I’ve got lots of errors when trying to run the notebook Defocus and Contrast Inversion.ipynb
in the docs/source/examples
folder found in the development branch.
Since I’m new to prysm
I decided to go on and stick to the new syntax and changes (which hopefully are here to stay!) and dig into the documentation to try to adapt said example notebook by myself.
Well I’ve not managed very far actually, besides importing the correct modules and dealing with the siemensstar object, I’m hitting a wall with this part:
pu = NollZernike(Z4=defocus_values[idx])
ps = PSF.from_pupil(pu, 4, norm='radiometric') # pu defaults to diameter of 1, this makes F/4
mt = MTF.from_psf(ps)
For the pupil, I used zernike.zernike_nm
and zernike.noll_to_nm
from the prysm.polynomials
module to convert from Z4 (defocus) to n,m, as follows (parameters for the grid yet to chose properly but this works):
x, y = make_xy_grid(64, diameter=1)
r, t = cart_to_polar(x, y)
znoll = noll_to_nm(4) #NollZernike(Z4=defocus_values[idx])
pu = zernike_nm(*znoll,r,t)
I can plot the pupil all right. But the first question arises: how can different amounts of defocus be taken into account as in the loop in the example notebook?
Moving on to PSF.from_pupil
, the doc says the following about the PSF module:
The PSF module has changed from being a core part of propagation usage to a module purely for computing criteria of PSFs, such as fwhm, centroid, etc. PSF has been removed
So now that prysm.psf
is no more about propagation but dimensional specifications of the psf, how can I create a PSF from the pupil just created with zernike_nm
?
Also,
MTF was removed, use mtf_from_psf()
And the documentation about mtf_from_psf()
explains:
mtf_from_psf(psf, dx=None) Compute the MTF from a given PSF. Parameters psf (prysm.RichData or numpy.ndarray) – object with data property having 2D data containing the psf, or the array itself
I suppose that if I had obtained the psf this part with the new mtf_from_psf
would be easy to deal with.
So it seems my issues boil down to my first 2 questions:
- how to create a pupil taking into account various amount of defocus?
- how to create a psf object (RichData or np.ndarray) from my pupil?
Thanks a lot in advance for any hints in this regard!
Ludo
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (4 by maintainers)
Top GitHub Comments
Hi,
First off - thanks for this issue, it is extremely valuable, and you’ve obviously done a huge amount of digging on your own. The shortest answer to your question is that this tutorial shows what you are asking about.
The docs are the final(ish) piece of the v0.20 development process and still very incomplete. It has been a very long time to rewrite all of the code and get it all tested again. On a compatibility note, prysm is pre-V1.0 and makes no compatibility guarantees, so I am free to do whatever I want to the API. Arguably, that’s better than other programs (say, Falco) that declared v1.0 a long time ago, but still release breaking changes in point releases, and go through several major versions per year. There is no point to a major version if it will be unsupported in three months!
The new design of prysm is indeed here to stay ad infinitum. Six years of different designs and the user experience for them fed into this design. prysm will become a bit more verbose and dissimilar to how a lot of people like their diffraction program written (pass the grid instead of
npix
, quelle horeur…). It is possible I or someone else will make a prysm-lite that makes a more terse object oriented API for common problems, I don’t know. The driving factor in this change is to make it easier to embed and extend prysm. Heavy object-oriented systems (like POPPY) require a ton of work to extend. A data oriented system (prysm v0.20+) is trivial to extend, because the data is the contract and not some object system.In your specific conversion…
The diameter should be two. The Zernike polynomials are orthogonal on r in [0,1]. r is a radius.
Your
pu
variable isn’t really a “pupil” though – it’s just a particular Zernike polynomial, evaluated on a particular grid. Theprysm.propagation.Wavefront.from_amp_and_phase
function will turn it into a “pupil” (complex wavefunction, which we know a priori exists in the pupil plane and thus “is” a pupil).Keep a copy of
z4 = zernike_nm(*znoll, r,t)
around and doWavefront.from_amp_and_phase(..., z4*2)
or z4 times any number. In general,prysm.polynomials.sum_of_2d_modes([z4], [123.456])
does what you want, but for a single mode you may as well scale it by itself.A weakness of the current design is that it is fairly verbose or error prone to sweep defocus with other phase errors. I am aware, I just don’t especially have a good design thought through. The way I would do it is to use my oracle knowledge of which mode index is defocus and futz with that:
This is the most computationally efficient way.
For anyone interested, I’ve updated my drafty notebook here.
I’ll close this issue as you have answered all my questions (and much more).
Thank you.