Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions specsim/fiberloss.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@ class GalsimFiberlossCalculator(object):
moffat_beta : float
Beta parameter value for the atmospheric PSF Moffat profile.
maximum_fft_size : int
Maximum size of FFT allowed.
5~ Maximum size of FFT allowed.
fiber_placement : array
Fiber placement position in microns. It is needed for dithering simulations.
"""
def __init__(self, fiber_diameter, wlen_grid, num_pixels=16,
oversampling=32, moffat_beta=3.5, maximum_fft_size=32767):
oversampling=32, moffat_beta=3.5, maximum_fft_size=32767,
fiber_placement=[0., 0.]):

self.wlen_grid = np.asarray(wlen_grid)
self.moffat_beta = moffat_beta
self.fiber_placement = fiber_placement

# Defer import to runtime.
import galsim
Expand All @@ -48,21 +52,20 @@ def __init__(self, fiber_diameter, wlen_grid, num_pixels=16,
# rather than on-sky angles.
scale = fiber_diameter / num_pixels
self.image = galsim.Image(num_pixels, num_pixels, scale=scale)

self.gsparams = galsim.GSParams(maximum_fft_size=32767)

# Prepare an anti-aliased image of the fiber aperture.
nos = num_pixels * oversampling
dxy = (np.arange(nos) + 0.5 - 0.5 * nos) / (0.5 * nos)
rsq = dxy ** 2 + dxy[:, np.newaxis] ** 2
dx = (np.arange(nos) + 0.5 - 0.5 * nos) / (0.5 * nos)# - self.fiber_placement[0]/0.5/fiber_diameter
dy = (np.arange(nos) + 0.5 - 0.5 * nos) / (0.5 * nos)# - self.fiber_placement[1]/0.5/fiber_diameter
rsq = dx ** 2 + dy[:, np.newaxis] ** 2
inside = (rsq <= 1).astype(float)
s0, s1 = inside.strides
blocks = numpy.lib.stride_tricks.as_strided(
inside, shape=(num_pixels, num_pixels, oversampling, oversampling),
strides=(oversampling * s0, oversampling * s1, s0, s1))
self.aperture = blocks.sum(axis=(2, 3)) / oversampling ** 2



def create_source(self, fractions, half_light_radius,
minor_major_axis_ratio, position_angle):
"""Create a model for the on-sky profile of a single source.
Expand Down Expand Up @@ -123,7 +126,7 @@ def create_source(self, fractions, half_light_radius,
def calculate(self, seeing_fwhm, scale, offset, blur_rms,
source_fraction, source_half_light_radius,
source_minor_major_axis_ratio, source_position_angle,
saved_images_file=None):
saved_images_file=None, verbosity=False):
"""Calculate the acceptance fractions for a set of fibers.

Parameters
Expand Down Expand Up @@ -214,8 +217,12 @@ def calculate(self, seeing_fwhm, scale, offset, blur_rms,
# this fiber location.
atmospheric_psf = seeing.transform(
scale[j, 0], 0, 0, scale[j, 1]).withFlux(1)
if verbosity:
print("Atmoshperic PSF: ", atmospheric_psf)
# Create the instrument PSF for this fiber and wavelength.
instrument_psf = galsim.Gaussian(sigma=blur_rms[j, i])
if verbosity:
print("Instrument PSF: ", instrument_psf)
if i == 0:
# Create the source profile for this fiber on the sky.
source_profile = self.create_source(
Expand All @@ -229,6 +236,8 @@ def calculate(self, seeing_fwhm, scale, offset, blur_rms,
else:
# Lookup the source model for this fiber.
source_profile = source_profiles[j]
if verbosity:
print("Source profile: ", source_profile)
# Convolve the source + instrument + astmosphere.
convolved = galsim.Convolve(
[instrument_psf, atmospheric_psf, source_profile],
Expand All @@ -238,10 +247,14 @@ def calculate(self, seeing_fwhm, scale, offset, blur_rms,
# TODO: compare method='no_pixel' and 'auto' for
# accuracy and speed.
draw_args = dict(image=self.image, method='auto')
if verbosity:
print("Draw args: ", draw_args)
print("Offsets: ", offsets)
print("GS params: ", self.gsparams)
convolved.drawImage(offset=offsets, **draw_args)
# Calculate the fiberloss fraction for this fiber and wlen.
fiberloss[j, i] = np.sum(self.image.array * self.aperture)

if saved_images_file is not None:
header['FIBER'] = j
header['WLEN'] = wlen
Expand Down Expand Up @@ -280,7 +293,8 @@ def calculate_fiber_acceptance_fraction(
focal_x, focal_y, wavelength, source, atmosphere, instrument,
source_types=None, source_fraction=None, source_half_light_radius=None,
source_minor_major_axis_ratio=None, source_position_angle=None,
oversampling=32, saved_images_file=None, saved_table_file=None):
oversampling=32, saved_images_file=None, saved_table_file=None,
fiber_placement=[0., 0.]):
"""Calculate the acceptance fraction for a single fiber.

The behavior of this function is customized by the instrument.fiberloss
Expand Down Expand Up @@ -345,6 +359,8 @@ def calculate_fiber_acceptance_fraction(
extension determines the file format, and .ecsv is recommended.
The saved file can then be used as a pre-tabulated input with
instrument.fiberloss.method = 'table'.
fiber_placement : array
Fiber placement position in microns. It is needed for the dithering simulations.

Returns
-------
Expand Down Expand Up @@ -464,7 +480,8 @@ def calculate_fiber_acceptance_fraction(
# with implicit units.
fiberloss_grid = calc.calculate(
seeing_fwhm,
scale.to(u.um / u.arcsec).value, offset.to(u.um).value,
scale.to(u.um / u.arcsec).value,
offset.to(u.um).value + fiber_placement,
blur.to(u.um).value,
source_fraction, source_half_light_radius,
source_minor_major_axis_ratio, source_position_angle,
Expand Down