From 001a925821ee845b590dd9da532fc7714463955b Mon Sep 17 00:00:00 2001 From: Tolga Yapici Date: Mon, 8 Apr 2019 10:05:03 -0400 Subject: [PATCH 1/2] fiberloss method is updated for dithering simulations --- specsim/fiberloss.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/specsim/fiberloss.py b/specsim/fiberloss.py index ab1a318..05d1816 100644 --- a/specsim/fiberloss.py +++ b/specsim/fiberloss.py @@ -33,12 +33,16 @@ class GalsimFiberlossCalculator(object): Beta parameter value for the atmospheric PSF Moffat profile. maximum_fft_size : int 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 @@ -48,13 +52,13 @@ 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( @@ -62,7 +66,6 @@ def __init__(self, fiber_diameter, wlen_grid, num_pixels=16, 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. @@ -280,7 +283,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 @@ -345,6 +349,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 ------- @@ -458,7 +464,8 @@ def calculate_fiber_acceptance_fraction( wlen_grid.to(u.Angstrom).value, instrument.fiberloss_num_pixels, oversampling, - atmosphere.seeing_moffat_beta) + atmosphere.seeing_moffat_beta, + fiber_placement=fiber_placement) # Calculate fiberloss fractions. Note that the calculator expects arrays # with implicit units. From 2a45d712392eb9c92ca0a29484b13f1d8fc7a41f Mon Sep 17 00:00:00 2001 From: Tolga Yapici Date: Tue, 12 Nov 2019 09:41:15 -0500 Subject: [PATCH 2/2] [skip ci] we move psfs rather than the aperture --- specsim/fiberloss.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/specsim/fiberloss.py b/specsim/fiberloss.py index 05d1816..4beeb06 100644 --- a/specsim/fiberloss.py +++ b/specsim/fiberloss.py @@ -32,7 +32,7 @@ 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. """ @@ -56,8 +56,8 @@ def __init__(self, fiber_diameter, wlen_grid, num_pixels=16, # Prepare an anti-aliased image of the fiber aperture. nos = num_pixels * oversampling - 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 + 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 @@ -65,7 +65,7 @@ def __init__(self, fiber_diameter, wlen_grid, num_pixels=16, 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. @@ -126,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 @@ -217,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( @@ -232,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], @@ -241,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 @@ -464,14 +474,14 @@ def calculate_fiber_acceptance_fraction( wlen_grid.to(u.Angstrom).value, instrument.fiberloss_num_pixels, oversampling, - atmosphere.seeing_moffat_beta, - fiber_placement=fiber_placement) + atmosphere.seeing_moffat_beta) # Calculate fiberloss fractions. Note that the calculator expects arrays # 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,