Add PSF model based on Zernike coefficients - #3056
Conversation
kosack
left a comment
There was a problem hiding this comment.
Looks good, but a couple of small requests:
- Can you add a citation to the docs to where the "Zernike" coefficients are defined?
- Just a question: you make all the parameters generic names like "z2-z6", but they could have nicer names, e.g. z2 could be
tilt_x, etc. Maybe that is because of how they are defined in the reference paper? If so, then adding the citation is fine, but otherwise, why not use human-readable names?
I've added a reference to Wikipedia. The original Zernike article is in German, and Noll's article is behind a paywall, but Wikipedia provides all the necessary information.
I would like to keep them as is (Zi), as this is the industry standard. For example, Ansys Zemax uses this notation, which makes it easy to copy or cross-check coefficient values directly from ray-tracing software. The docstrings for each parameter already contain the human-readable descriptions, so I think it's a good compromise between readability and standardization. |
| self.z5.tel[tel_id].to_value(u.m) + z5_theta2_m_per_deg2 * theta2, | ||
| self.z6.tel[tel_id].to_value(u.m) + z6_theta2_m_per_deg2 * theta2, | ||
| self.z7.tel[tel_id].to_value(u.m) + coma_x, | ||
| self.z8.tel[tel_id].to_value(u.m) + coma_y, |
There was a problem hiding this comment.
According to wikipedia link, the Noll coefficient z7 is the vertical and z8 the horizontal. I think then you need to swap the comas, I mean add coma_x to z8 and coma_y to z7
There was a problem hiding this comment.
This was an artifact of some experimenting with the internal to the model coordinate transforms, I've simplified it.
| phase_safe[mask] = phase[mask] | ||
| pupil = aperture * np.exp(1j * phase_safe) | ||
| field = fft2(pupil) | ||
| intensity += weight * fftshift(np.abs(field) ** 2) |
There was a problem hiding this comment.
The angular coordinates depend on the wavelength. I think here we ignore this dependence, it should be accounted for before the final summation.
|
Hi @mexanick , thanks a lot for this. I asked for one more thing to be clarified. Other than that looks good to me. |
Thanks, it is fixed now. I also found and fixed an error in the astigmatism application. An updated PSF map: |
…igmatism corrections
277728b to
663d9ed
Compare
|





Summary
Adds
ZernikePSFModel, toctapipe.instrument.opticsalongside the existingComaPSFModel.Unlike
ComaPSFModel's parametric radial/polar scaling approach, this model reconstructs the optical wavefront directly from Zernike coefficients (using Noll indexing) and propagates it through Fraunhofer diffraction (FFT of the aberrated pupil).Implementation details
pupil_size,pupil_diameter_fraction) with a soft-edged aperture (pupil_edge_softness) to avoid FFT ringing.z2–z11(tilt, defocus, astigmatism, coma, trefoil, spherical), using thezernikepackage'sRZernfor polynomial evaluation.z7_theta,z8_theta), astigmatism grows quadratically with θ² (z5_theta2,z6_theta2).wavelength_sampleswavelengths betweenwavelength_minandwavelength_max, weighted by acherenkov_spectrum_indexpower law (dN/dλ ∝ λ^-index).focal_plane_smoothing_sigma_pix) to the resulting intensity to approximate pixel/light-guide response.pdf(tel_id, lon, lat, lon0, lat0)interface shared withComaPSFModel, interpolating the precomputed PSF grid viascipy.ndimage.map_coordinatesand normalizing to unit integral.