spacetelescope / webbpsf

James Webb Space Telescope PSF simulation tool
https://webbpsf.readthedocs.io
BSD 3-Clause "New" or "Revised" License
119 stars 63 forks source link

Python supported versions #547

Closed tddesjardins closed 2 years ago

tddesjardins commented 2 years ago

The installation instructions simply say that Python 3.7+ is required, but I tried running this notebook (https://github.com/spacetelescope/roman_tools/blob/develop/notebooks/WebbPSF-Roman_Tutorial.ipynb) with Python 3.10.4, and I received the following exception:

[  poppy] Computing wavelength weights using synthetic photometry for F062...
[webbpsf] Using pupil mask 'SKINNY' and detector 'SCA01'.
[  poppy] PSF calc using fov_arcsec = 5.000000, oversample = 4, number of wavelengths = 10
[webbpsf] Creating optical system model:
[  poppy] Initialized OpticalSystem: Roman+WFI
[  poppy] Roman Entrance Pupil: Loaded amplitude transmission from /Users/desjard/Downloads/webbpsf-data/WFI/pupils/RST_WIM_Filter_skinny_SCA_1.fits.gz
[  poppy] Roman Entrance Pupil: Loaded OPD from /Users/desjard/Downloads/webbpsf-data/upscaled_HST_OPD.fits
[  poppy] Added pupil plane: Roman Entrance Pupil
[  poppy] Added coordinate inversion plane: OTE exit pupil
[  poppy] Added pupil plane: Field Dependent Aberration (SCA01)
[  poppy] Added detector with pixelscale=0.11 and oversampling=4: WFI detector
[  poppy] Calculating PSF with 10 wavelengths
[  poppy]  Propagating wavelength = 4.8975e-07 m
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/webbpsf/jupyter_gui.py:184, in show_notebook_interface_wfi.<locals>.calc(*args)
    182     source = poppy.specFromSpectralType(source_selection.value)
    183     _log.debug("Got source type {}: {}".format(source_selection.value, source))
--> 184     psf = instrument.calc_psf(
    185         source=source,
    186         display=True,
    187         outfile=OUTPUT_FILENAME,
    188         overwrite=True
    189     )
    190 fig, (ax_oversamp, ax_detsamp) = plt.subplots(1, 2)
    191 poppy.display_PSF(psf, ax=ax_oversamp)

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/webbpsf/roman.py:305, in RomanInstrument.calc_psf(self, outfile, source, nlambda, monochromatic, fov_arcsec, fov_pixels, oversample, detector_oversample, fft_oversample, overwrite, display, save_intermediates, return_intermediates, normalize, add_distortion, crop_psf)
    300     raise AttributeError('`add_distortion` and `crop_psf` still under '
    301                          'development for Roman WFI')
    302     # return default values to True once implemented
    303 
    304 # Run poppy calc_psf
--> 305 psf = webbpsf_core.SpaceTelescopeInstrument.calc_psf(self, outfile=outfile, source=source, nlambda=nlambda,
    306                                         monochromatic=monochromatic, fov_arcsec=fov_arcsec,
    307                                         fov_pixels=fov_pixels, oversample=oversample,
    308                                         detector_oversample=detector_oversample, fft_oversample=fft_oversample,
    309                                         overwrite=overwrite, display=display,
    310                                         save_intermediates=save_intermediates,
    311                                         return_intermediates=return_intermediates, normalize=normalize)
    313 return psf

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/instrument.py:265, in Instrument.calc_psf(self, outfile, source, nlambda, monochromatic, fov_arcsec, fov_pixels, oversample, detector_oversample, fft_oversample, overwrite, display, save_intermediates, return_intermediates, normalize)
    263 self._check_for_aliasing(wavelens)
    264 # and use it to compute the PSF (the real work happens here, in code in poppy.py)
--> 265 result = self.optsys.calc_psf(wavelens, weights, display_intermediates=display, display=display,
    266                               save_intermediates=save_intermediates, return_intermediates=return_intermediates,
    267                               normalize=normalize)
    269 if return_intermediates:  # this implies we got handed back a tuple, so split it apart
    270     result, intermediates = result

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/utils.py:1420, in BackCompatibleQuantityInput.__call__.<locals>.unit_check_wrapper(*func_args, **func_kwargs)
   1416 # Call the original function with any equivalencies in force.
   1417 with add_enabled_equivalencies(self.equivalencies):
   1418     # print("Args:   {}".format(bound_args.args))
   1419     # print("KWArgs: {}".format(bound_args.kwargs))
-> 1420     return wrapped_function(*bound_args.args, **bound_args.kwargs)

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/poppy_core.py:1669, in BaseOpticalSystem.calc_psf(self, wavelength, weight, save_intermediates, save_intermediates_what, display, return_intermediates, return_final, source, normalize, display_intermediates, inwave)
   1667     plt.clf()
   1668 for wlen, wave_weight in zip(wavelength, normwts):
-> 1669     mono_psf, mono_intermediate_wfs = self.propagate_mono(
   1670         wlen,
   1671         retain_intermediates=retain_intermediates,
   1672         retain_final=return_final,
   1673         display_intermediates=display_intermediates,
   1674         normalize=normalize,
   1675         inwave=inwave
   1676     )
   1678     if outfits is None:
   1679         # for the first wavelength processed, set up the arrays where we accumulate the output
   1680         outfits = mono_psf

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/utils.py:1420, in BackCompatibleQuantityInput.__call__.<locals>.unit_check_wrapper(*func_args, **func_kwargs)
   1416 # Call the original function with any equivalencies in force.
   1417 with add_enabled_equivalencies(self.equivalencies):
   1418     # print("Args:   {}".format(bound_args.args))
   1419     # print("KWArgs: {}".format(bound_args.kwargs))
-> 1420     return wrapped_function(*bound_args.args, **bound_args.kwargs)

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/poppy_core.py:1807, in BaseOpticalSystem.propagate_mono(self, wavelength, normalize, retain_intermediates, retain_final, display_intermediates, inwave)
   1805     wavefront, intermediate_wfs = self.propagate(wavefront, **kwargs)
   1806 else:
-> 1807     wavefront = self.propagate(wavefront, **kwargs)
   1808     intermediate_wfs = []
   1810 if (not retain_intermediates) & retain_final:  # return the full complex wavefront of the last plane.

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/poppy_core.py:2155, in OpticalSystem.propagate(self, wavefront, normalize, return_intermediates, display_intermediates)
   2153 # The actual propagation:
   2154 wavefront.propagate_to(optic)
-> 2155 wavefront *= optic
   2157 # Normalize if appropriate:
   2158 if normalize.lower() == 'first' and wavefront.current_plane_index == 1:  # set entrance plane to 1.

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/poppy_core.py:181, in BaseWavefront.__imul__(self, optic)
    178     self.location = 'at ' + optic.name
    179     return self
--> 181 phasor = optic.get_phasor(self)
    183 if not np.isscalar(phasor) and phasor.size > 1:
    184     assert self.wavefront.shape == phasor.shape, "Phasor shape {} does not match wavefront shape {}".format(
    185         phasor.shape, self.wavefront.shape)

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/optics.py:143, in AnalyticOpticalElement.get_phasor(self, wave)
    140         return np.asarray(result, _complex())
    142 else:
--> 143     return self.get_transmission(wave) * np.exp(1.j * self.get_opd(wave) * scale)

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/webbpsf/roman.py:126, in FieldDependentAberration.get_opd(self, wave)
    124     wavelength = wave.wavelength
    125 self.coefficients = wavelength * self.get_aberration_terms(wavelength)
--> 126 return super().get_opd(wave)

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/wfe.py:41, in _check_wavefront_arg.<locals>.wrapper(*args, **kwargs)
     39     raise ValueError("The first argument must be a Wavefront or FresnelWavefront object.")
     40 else:
---> 41     return f(*args, **kwargs)

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/wfe.py:244, in ZernikeWFE.get_opd(self, wave)
    236         combined_zernikes += k_in_m * zernike.zernike1(
    237             j,
    238             rho=rho,
   (...)
    241             noll_normalize=True
    242         )
    243     else:
--> 244         combined_zernikes += k_in_m * zernike.cached_zernike1(
    245             j,
    246             wave.shape,
    247             pixelscale_m,
    248             self.radius.to(u.meter).value,
    249             outside=0.0,
    250             noll_normalize=True
    251         )
    253 combined_zernikes[aperture_intensity==0] = 0
    254 return combined_zernikes

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/zernike.py:310, in cached_zernike1(j, shape, pixelscale, pupil_radius, outside, noll_normalize)
    307 theta = np.arctan2(y / pupil_radius, x / pupil_radius)
    309 n, m = noll_indices(j)
--> 310 result = zernike(n, m, rho=rho, theta=theta, outside=outside, noll_normalize=noll_normalize)
    311 result.flags.writeable = False  # don't let caller modify cached copy in-place
    312 return result

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/zernike.py:259, in zernike(n, m, npix, rho, theta, outside, noll_normalize, **kwargs)
    257 elif m > 0:
    258     norm_coeff = np.sqrt(2) * np.sqrt(n + 1) if noll_normalize else 1
--> 259     zernike_result = norm_coeff * R(n, m, rho) * np.cos(np.abs(m) * theta) * aperture
    260 else:
    261     norm_coeff = np.sqrt(2) * np.sqrt(n + 1) if noll_normalize else 1

File ~/miniconda3/envs/pandeia_test/lib/python3.10/site-packages/poppy/zernike.py:174, in R(n, m, rho)
    171 else:
    172     for k in range(int((n - m) / 2) + 1):
    173         coef = ((-1) ** k * factorial(n - k) /
--> 174                 (factorial(k) * factorial((n + m) / 2. - k) * factorial((n - m) / 2. - k)))
    175         output += coef * rho ** (n - 2 * k)
    176     return output

TypeError: 'float' object cannot be interpreted as an integer

Testing this with 3.7.13 does not produce this exception. Python 3.7 does, however, give me a different exception from poppy:

AttributeError                            Traceback (most recent call last)
~/miniconda3/envs/pandeia37/lib/python3.7/site-packages/webbpsf/jupyter_gui.py in calc(*args)
    189             )
    190         fig, (ax_oversamp, ax_detsamp) = plt.subplots(1, 2)
--> 191         poppy.display_PSF(psf, ax=ax_oversamp)
    192         poppy.display_PSF(psf, ax=ax_detsamp, ext='DET_SAMP')
    193         progress.visible = None

AttributeError: module 'poppy' has no attribute 'display_PSF'

For reference, in both cases I am running webbpsf==1.0.0 and poppy==1.0.2.

tddesjardins commented 2 years ago

Sorry, I now see that this is being documented at https://github.com/spacetelescope/poppy/issues/502. I will comment on that issue. This one can be closed.