Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How to create a psf from a pupil with the new (v0.20) style?

See original GitHub issue


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:

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?


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!


Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

brandondubecommented, Jun 11, 2021


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…

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)

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. The prysm.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).

how can different amounts of defocus be taken into account as in the loop in the example notebook?

Keep a copy of z4 = zernike_nm(*znoll, r,t) around and do Wavefront.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:

nms = [polynomials.noll_to_nm(j) for j in range(2,25)] # tilt to some higher order thing
idefocus = nms.index((4,0))

basis_set = list(polynomials.zernike_nm_sequence(nms, r, t)
coefs = np.random.rand(len(nms))
c2 = coefs.copy()
# nominal
phs = polynomials.sum_of_2d_modes(basis_set, coefs)

# defocused
c2[idefocus] = coefs[idefocus] + 2 # or whatever
phs2 = polynomials.sum_of_2d_modes(basis_set, c2)

This is the most computationally efficient way.

diplodocuslonguscommented, Jun 17, 2021

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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Phase Retrieval III: Making Conjugate Gradient Great
Before we can tackle the reverse mode problem (PSF => phase) we need to do the forward mode problem (phase => PSF). For...
Read more >
PISA 2018: Insights and Interpretations | OECD
The Programme for International Student Assessment. (PISA) changed this. The idea behind PISA lay in testing the knowledge and skills of students directly,....
Read more >
Reviewing the evidence on how teacher professional ...
First, professional devel- opment enhances teacher knowledge and skills. Second, better knowledge and skills improve classroom teaching. Third, improved ...
Read more >
(PDF) Creating the safe learning environment - ResearchGate
being embarrassed by the teacher, or by other learners in the new learning environment. As adolescents, learners may have seen school as a...
Read more >
STUDENT HANDBOOK 2022-23 Classroom Hours Secondary
The technology center will make public in its Great Plains Technology Center ... The Federal Family Educational Rights and Privacy of 1974, 20...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Post

No results found

github_iconTop Related Hashnode Post

No results found