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

Hello,

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

1reaction
brandondubecommented, Jun 11, 2021

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.

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

0reactions
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.

#### Top 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...
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,....
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 ...
(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...
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...

#### Troubleshoot Live Code

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