Skip to content

Commit

Permalink
Merge branch 'develop' into dependabot/pip/scipy-1.11.1
Browse files Browse the repository at this point in the history
  • Loading branch information
BradleySappington authored Aug 29, 2023
2 parents c27d01b + e0337e2 commit a5b4dad
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 50 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci_workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v2
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python }}

Expand All @@ -73,6 +73,6 @@ jobs:

- name: Upload coverage to codecov
if: ${{ contains(matrix.toxenv,'-cov') }}
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
4 changes: 0 additions & 4 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,3 @@

TESTED_VERSIONS['poppy'] = __version__

## Uncomment the following line to treat all DeprecationWarnings as
## exceptions
# from astropy.tests.helper import enable_deprecations_as_exceptions
# enable_deprecations_as_exceptions()
56 changes: 28 additions & 28 deletions poppy/fresnel.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
_log = logging.getLogger('poppy')


__all__ = ['QuadPhase', 'QuadraticLens', 'FresnelWavefront', 'FresnelOpticalSystem',
__all__ = ['QuadPhase', 'QuadraticLens', 'FresnelWavefront', 'FresnelOpticalSystem',
'FixedSamplingImagePlaneElement']


Expand Down Expand Up @@ -169,41 +169,41 @@ def __init__(self,

class FixedSamplingImagePlaneElement(FITSOpticalElement):
'''
This class allows the definition of focal plane masks using .fits files that will be applied to a
This class allows the definition of focal plane masks using .fits files that will be applied to a
wavefront via an FFT/MFT sequence to achieve the correct sampling at the assumed focal plane.
This element will only function as an intermediate planetype due to pixelscale and display functionality
when propagating to this plane.
Note: if an image planetype were to be used, the wavefront at this plane may have infinite pixelscales,
when propagating to this plane.
Note: if an image planetype were to be used, the wavefront at this plane may have infinite pixelscales,
making it impossible to display the wavefront with extents.
The method used to apply this element requires additional information from the user that is not required
for FITSOpticalElements. These additional parameters are listed below.
The method used to apply this element requires additional information from the user that is not required
for FITSOpticalElements. These additional parameters are listed below.
Parameters not in FITSOpticalElement
----------
wavelength_c: astropy.quantity
Central wavelength of the user's system, required in order to
convert the pixelscale to units of lambda/D and scale the
pixelscale of the element based on the wavelength being propagated. If this parameter is left as None,
wavelength_c: astropy.quantity
Central wavelength of the user's system, required in order to
convert the pixelscale to units of lambda/D and scale the
pixelscale of the element based on the wavelength being propagated. If this parameter is left as None,
the pixel scale can be read from the FITS header keyword PIXELSCL, if that keyword exists in the provided file.
entrance_pupil_diam: astropy.quantity
Entrance pupil diameter of the system, required to convert the
pixelscale to units of lambda/D. If this parameter is left as None, the pixel scale can be
read from the FITS header keyword PIXELSCL, if that keyword exists in the provided file.
Entrance pupil diameter of the system, required to convert the
pixelscale to units of lambda/D. If this parameter is left as None, the pixel scale can be
read from the FITS header keyword PIXELSCL, if that keyword exists in the provided file.
pixelscale: float
pixelscale value in units of arcsec/pix. If this parameter is left as None, the pixel scale can be
pixelscale value in units of arcsec/pix. If this parameter is left as None, the pixel scale can be
read from the FITS header keyword PIXELSCL, if that keyword exists in the provided file.
centering: str
What type of centering to use for the MFTs, see MFT documentation
What type of centering to use for the MFTs, see MFT documentation
for more information. Default is 'ADJUSTABLE'.
'''
def __init__(self, name="unnamed FPM element", transmission=None, opd=None, opdunits=None,
planetype=PlaneType.intermediate,
wavelength_c=None, entrance_pupil_diam=None, pixelscale=None, centering='ADJUSTABLE',
**kwargs):

FITSOpticalElement.__init__(self, name=name, transmission=transmission, opd=opd, opdunits=opdunits,
planetype=planetype, **kwargs)

Expand Down Expand Up @@ -909,7 +909,7 @@ def __imul__(self, optic):
return self
elif isinstance(optic, FixedSamplingImagePlaneElement):
# Special case: if we have an FPM, call the routine for that,
# which will apply an amplitude transmission to the wavefront.
# which will apply an amplitude transmission to the wavefront.
self.apply_image_plane_fftmft(optic)
return self
else:
Expand Down Expand Up @@ -1045,31 +1045,31 @@ def apply_lens_power(self, optic, ignore_wavefront=False):
def apply_image_plane_fftmft(self, optic):
"""
Apply a focal plane mask using fft and mft methods to highly sample at the focal plane.
Parameters
----------
optic : FixedSamplingImagePlaneElement
"""
_log.debug("------ Applying FixedSamplingImagePlaneElement using FFT and MFT sequence ------")

# readjust pixelscale to wavelength being propagated
fpm_pxscl_lamD = ( optic.pixelscale_lamD * optic.wavelength_c.to(u.meter) / self.wavelength.to(u.meter) ).value
fpm_pxscl_lamD = ( optic.pixelscale_lamD * optic.wavelength_c.to(u.meter) / self.wavelength.to(u.meter) ).value

# get the fpm phasor either using numexpr or numpy
scale = 2. * np.pi / self.wavelength.to(u.meter).value
if accel_math._USE_NUMEXPR:
_log.debug("Calculating FPM phasor from numexpr.")
trans = optic.get_transmission(self)
opd = optic.get_opd(self)
fpm_phasor = ne.evaluate("trans * exp(1.j * opd * scale)")
fpm_phasor = ne.evaluate("trans * exp(1j * opd * scale)")
else:
_log.debug("Calculating FPM phasor with Numpy/CuPy.")
fpm_phasor = optic.get_transmission(self) * xp.exp(1.j * optic.get_opd(self) * scale)
fpm_phasor = optic.get_transmission(self) * xp.exp(1j * optic.get_opd(self) * scale)

nfpm = fpm_phasor.shape[0]
n = self.wavefront.shape[0]

nfpmlamD = nfpm*fpm_pxscl_lamD*self.oversample

mft = poppy.matrixDFT.MatrixFourierTransform(centering=optic.centering)
Expand All @@ -1081,7 +1081,7 @@ def apply_image_plane_fftmft(self, optic):
self.wavefront = mft.inverse(self.wavefront, nfpmlamD, n) # MFT to virtual pupil
self.wavefront = accel_math.fft_2d(self.wavefront, forward=True, fftshift=True) # FFT back to focal plane
self.wavefront = accel_math._fftshift(self.wavefront)

_log.debug("------ FixedSamplingImagePlaneElement: " + str(optic.name) + " applied ------")

def _resample_wavefront_pixelscale(self, detector):
Expand Down
20 changes: 10 additions & 10 deletions poppy/poppy_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -797,15 +797,15 @@ def interpolator(arr):
Bind arguments to scipy's RectBivariateSpline function.
For data on a regular 2D grid, RectBivariateSpline is more efficient than interp2d.
"""
return scipy.interpolate.RectBivariateSpline(x_in, y_in, arr,
return scipy.interpolate.RectBivariateSpline(x_in, y_in, arr,
kx=detector.interp_order, ky=detector.interp_order)

# Interpolate real and imaginary parts separately
real_resampled = interpolator(cropped_wf.real)(x_out, y_out)
imag_resampled = interpolator(cropped_wf.imag)(x_out, y_out)
new_wf = xp.array(real_resampled + 1j * imag_resampled)
else:
# cupyx does not have RectBivariateSpline or interp2d so wavefront resampling
# cupyx does not have RectBivariateSpline or interp2d so wavefront resampling
# is implemented with map_coordinates
#wf_xmin = pixscale * cropped_wf.shape[0]/2
# Note, carefully handle the offset-by-one to be consistent with
Expand Down Expand Up @@ -916,7 +916,7 @@ def rotate(self, angle=0.0):
# arbitrary free rotation with interpolation
rot_real = _scipy.ndimage.rotate(self.wavefront.real, -angle, reshape=False) # negative = CCW
rot_imag = _scipy.ndimage.rotate(self.wavefront.imag, -angle, reshape=False)
self.wavefront = rot_real + 1.j * rot_imag
self.wavefront = rot_real + 1j * rot_imag

self.history.append('Rotated by {:.2f} degrees, CCW'.format(angle))

Expand Down Expand Up @@ -2471,11 +2471,11 @@ def get_transmission(self, wave):

def get_opd(self, wave):
""" Return the optical path difference, given a wavelength.
In this base class instance, the wavefront parameter 'wave' is not used,
In this base class instance, the wavefront parameter 'wave' is not used,
and the .opd attribute of the optic is returned directly.
Subclasses may change this behavior, for instance to evaluate
optical aberrations on the sampling defined for that wavefront,
optical aberrations on the sampling defined for that wavefront,
or to compute the wavelength-dependent aberrations of a refractive optic.
Parameters
Expand Down Expand Up @@ -2519,7 +2519,7 @@ def get_phasor(self, wave):
if hasattr(self, '_resampled_scale') and abs(
self._resampled_scale - wave.pixelscale) / self._resampled_scale >= float_tolerance:
# we already did this same resampling, so just re-use it!
self.phasor = self._resampled_amplitude * xp.exp(1.j * self._resampled_opd * scale)
self.phasor = self._resampled_amplitude * xp.exp(1j * self._resampled_opd * scale)
else:
# raise NotImplementedError("Need to implement resampling.")
zoom = (self.pixelscale / wave.pixelscale).decompose().value
Expand Down Expand Up @@ -2558,16 +2558,16 @@ def get_phasor(self, wave):
_log.debug("trimmed a border of {:d} x {:d} pixels from "
"optic to match the wavefront".format(border_x, border_y))

self.phasor = self._resampled_amplitude * xp.exp(1.j * self._resampled_opd * scale)
self.phasor = self._resampled_amplitude * xp.exp(1j * self._resampled_opd * scale)

else:
# compute the phasor directly, without any need to rescale.
if accel_math._USE_NUMEXPR:
trans = self.get_transmission(wave)
opd = self.get_opd(wave)
self.phasor = ne.evaluate("trans * exp(1.j * opd * scale)")
self.phasor = ne.evaluate("trans * exp(1j * opd * scale)")
else:
self.phasor = self.get_transmission(wave) * xp.exp(1.j * self.get_opd(wave) * scale)
self.phasor = self.get_transmission(wave) * xp.exp(1j * self.get_opd(wave) * scale)

# check whether we need to pad or crop the array before returning or not.
# note: do not pad the phasor if it's just a scalar!
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ test = [
]
docs = [
"nbsphinx",
"sphinx<7.0",
"stsci_rtd_theme",
"sphinx-astropy",
"sphinx_rtd_theme",
"sphinx-automodapi",
"sphinx-issues",
"tomli; python_version <\"3.11\"",
Expand Down
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
astropy==5.2.2
matplotlib==3.7.1
numpy==1.24.3
astropy==5.3.2
matplotlib==3.7.2
numpy==1.25.2
scipy==1.11.1
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ deps=
syn: synphot
legacy: numpy==1.20.0
legacy: astropy==4.3.0
legacy: scipy==1.10.1
latest: -rrequirements.txt
astropydev: git+https://github.com/astropy/astropy
numexpr: numexpr>=2.6.0
Expand All @@ -33,7 +34,6 @@ commands=
basepython= python3.10
deps=
sphinx
sphinx_rtd_theme
stsci_rtd_theme
sphinx-automodapi
sphinx-issues
Expand Down

0 comments on commit a5b4dad

Please sign in to comment.