Exemplo n.º 1
0
    def __init__(self):
        self._camera = lsst_camera()
        self._pixel_transformer = DMtoCameraPixelTransformer()
        self._z_gen = ZernikePolynomialGenerator()

        self._rr = 500.0  # radius in mm of circle containing LSST focal plane;
        # make it a little bigger to accommodate any slop in
        # the conversion from focal plane coordinates back to
        # pupil coordinates (in case the optical distortions
        # cause points to cross over the actual boundary of
        # the focal plane)

        self._band_to_int = {}
        self._band_to_int['u'] = 0
        self._band_to_int['g'] = 1
        self._band_to_int['r'] = 2
        self._band_to_int['i'] = 3
        self._band_to_int['z'] = 4
        self._band_to_int['y'] = 5

        self._int_to_band = 'ugrizy'

        self._n_grid = []
        self._m_grid = []

        # 2018 May 8
        # During development, I found that there was negligible
        # improvement in the fit when allowing n>4, so I am
        # hard-coding the limit of n=4 here.
        for n in range(4):
            for m in range(-n, n + 1, 2):
                self._n_grid.append(n)
                self._m_grid.append(m)

        self._build_transformations()
Exemplo n.º 2
0
    def test_orthogonality(self):
        """
        Test that ZernikePolynomialGenerator returns
        polynomials that are orthogonal on the unit disc
        """

        polynomials = {}
        z_gen = ZernikePolynomialGenerator()

        for n in range(3):
            for m in range(-n, n + 1, 2):
                vals = np.zeros(len(self.r_grid), dtype=float)
                for ii, (rr, pp) in enumerate(zip(self.r_grid, self.phi_grid)):
                    vals[ii] = z_gen.evaluate(rr, pp, n, m)
                nm_tuple = (n, m)
                polynomials[nm_tuple] = vals

        p_keys = list(polynomials.keys())
        for ii in range(len(p_keys)):
            p1_name = p_keys[ii]
            p1 = polynomials[p1_name]
            integral = (p1 * p1 * self.r_grid * self.d_r * self.d_phi).sum()
            normed_integral = integral / z_gen.norm(p1_name[0], p1_name[1])
            self.assertLess(np.abs(normed_integral - 1.0), 0.04)
            for jj in range(ii + 1, len(p_keys)):
                p2_name = p_keys[jj]
                p2 = polynomials[p2_name]
                dot = (p1 * p2 * self.r_grid * self.d_r * self.d_phi).sum()
                msg = '\n%s norm %e\n dot %e\n' % (p1_name, integral, dot)
                self.assertLess(np.abs(dot / integral), 0.01, msg=msg)
Exemplo n.º 3
0
    def test_Zernike_origin(self):
        """
        Test that ZernikePolynomialGenerator is well-behaved
        at r=0
        """
        n = 4
        m = 2
        z_gen = ZernikePolynomialGenerator()
        ans = z_gen.evaluate(0.0, 1.2, n, m)
        self.assertEqual(ans, 0.0)
        ans = z_gen.evaluate(np.array([0.0, 0.0]), np.array([1.2, 2.1]), n, m)

        np.testing.assert_array_equal(ans, np.zeros(2, dtype=float))
        ans = z_gen.evaluate_xy(0.0, 0.0, n, m)
        self.assertEqual(ans, 0.0)
        ans = z_gen.evaluate_xy(np.zeros(2, dtype=float),
                                np.zeros(2, dtype=float), n, m)
        np.testing.assert_array_equal(ans, np.zeros(2, dtype=float))

        n = 0
        m = 0
        ans = z_gen.evaluate(0.0, 1.2, n, m)
        self.assertEqual(ans, 1.0)
        ans = z_gen.evaluate(np.array([0.0, 0.0]), np.array([1.2, 2.1]), n, m)

        np.testing.assert_array_equal(ans, np.ones(2, dtype=float))
        ans = z_gen.evaluate_xy(0.0, 0.0, n, m)
        self.assertEqual(ans, 1.0)
        ans = z_gen.evaluate_xy(np.zeros(2, dtype=float),
                                np.zeros(2, dtype=float), n, m)
        np.testing.assert_array_equal(ans, np.ones(2, dtype=float))
Exemplo n.º 4
0
 def test_zeros(self):
     """
     Test that ZernikePolynomialGenerator returns zero
     when values of n and m require it.
     """
     rng = np.random.RandomState(88)
     z_gen = ZernikePolynomialGenerator()
     for n in range(4):
         for m in range(-(n - 1), n, 2):
             r = rng.random_sample()
             phi = rng.random_sample() * 2.0 * np.pi
             self.assertAlmostEqual(0.0, z_gen.evaluate(r, phi, n, m), 10)
Exemplo n.º 5
0
    def test_array(self):
        """
        Test that ZernikePolynomialGenerator can handle arrays of inputs
        """
        z_gen = ZernikePolynomialGenerator()
        n = 2
        m = -2
        val_arr = z_gen.evaluate(self.r_grid_small, self.phi_grid_small, n, m)
        self.assertEqual(len(val_arr), len(self.r_grid_small))
        for ii, (rr,
                 pp) in enumerate(zip(self.r_grid_small, self.phi_grid_small)):

            vv = z_gen.evaluate(rr, pp, n, m)
            self.assertAlmostEqual(vv, val_arr[ii], 14)
Exemplo n.º 6
0
 def test_xy(self):
     """
     Test that ZernikePolynomialGenerator can handle Cartesian coordinates
     """
     n = 4
     m = 2
     z_gen = ZernikePolynomialGenerator()
     x = self.r_grid_small * np.cos(self.phi_grid_small)
     y = self.r_grid_small * np.sin(self.phi_grid_small)
     val_arr = z_gen.evaluate_xy(x, y, n, m)
     self.assertGreater(np.abs(val_arr).max(), 1.0e-6)
     for ii, (rr,
              pp) in enumerate(zip(self.r_grid_small, self.phi_grid_small)):
         vv = z_gen.evaluate(rr, pp, n, m)
         self.assertAlmostEqual(vv, val_arr[ii], 14)
Exemplo n.º 7
0
    def __init__(self):
        self._camera = lsst_camera()
        self._pixel_transformer = DMtoCameraPixelTransformer()
        self._z_gen = ZernikePolynomialGenerator()

        self._rr = 500.0  # radius in mm of circle containing LSST focal plane;
                          # make it a little bigger to accommodate any slop in
                          # the conversion from focal plane coordinates back to
                          # pupil coordinates (in case the optical distortions
                          # cause points to cross over the actual boundary of
                          # the focal plane)

        self._band_to_int = {}
        self._band_to_int['u'] = 0
        self._band_to_int['g'] = 1
        self._band_to_int['r'] = 2
        self._band_to_int['i'] = 3
        self._band_to_int['z'] = 4
        self._band_to_int['y'] = 5

        self._int_to_band = 'ugrizy'

        self._n_grid = []
        self._m_grid = []

        # 2018 May 8
        # During development, I found that there was negligible
        # improvement in the fit when allowing n>4, so I am
        # hard-coding the limit of n=4 here.
        for n in range(4):
            for m in range(-n, n+1, 2):
                self._n_grid.append(n)
                self._m_grid.append(m)

        self._build_transformations()
Exemplo n.º 8
0
    def test_xy_one_at_a_time(self):
        """
        Test that ZernikePolynomialGenerator can handle
        scalar Cartesian coordinates (as opposed to arrays
        of Cartesian coordinates)
        """
        n = 4
        m = 2
        z_gen = ZernikePolynomialGenerator()
        x = self.r_grid_small * np.cos(self.phi_grid_small)
        y = self.r_grid_small * np.sin(self.phi_grid_small)

        for ii in range(len(self.r_grid_small)):
            vv_r = z_gen.evaluate(self.r_grid_small[ii],
                                  self.phi_grid_small[ii], n, m)
            vv_xy = z_gen.evaluate_xy(x[ii], y[ii], n, m)
            self.assertAlmostEqual(vv_r, vv_xy, 14)
            self.assertIsInstance(vv_xy, numbers.Number)
Exemplo n.º 9
0
 def test_r_greater_than_one(self):
     """
     Test that the expected error is raised if we try to evaluate
     the Zernike polynomial with r>1
     """
     z_gen = ZernikePolynomialGenerator()
     vv = z_gen.evaluate(1.2, 2.1, 2, 0)
     self.assertTrue(np.isnan(vv))
     vv = z_gen.evaluate(np.array([0.1, 0.5, 1.2]),
                         np.array([0.1, 0.2, 0.3]), 2, -2)
     self.assertTrue(np.isnan(vv[2]))
     self.assertFalse(np.isnan(vv[0]))
     self.assertFalse(np.isnan(vv[1]))
     vv = z_gen.evaluate_xy(1.1, 1.2, 4, -2)
     self.assertTrue(np.isnan(vv))
     vv = z_gen.evaluate_xy(np.array([0.1, 0.2, 0.3]),
                            np.array([0.1, 1.0, 0.1]), 4, 2)
     self.assertTrue(np.isnan(vv[1]))
     self.assertFalse(np.isnan(vv[0]))
     self.assertFalse(np.isnan(vv[2]))
Exemplo n.º 10
0
class LsstZernikeFitter(object):
    """
    This class will fit and then apply the Zernike polynomials needed
    to correct the FIELD_ANGLE to FOCAL_PLANE transformation for the
    filter-dependent part.
    """

    def __init__(self):
        self._camera = lsst_camera()
        self._pixel_transformer = DMtoCameraPixelTransformer()
        self._z_gen = ZernikePolynomialGenerator()

        self._rr = 500.0  # radius in mm of circle containing LSST focal plane;
                          # make it a little bigger to accommodate any slop in
                          # the conversion from focal plane coordinates back to
                          # pupil coordinates (in case the optical distortions
                          # cause points to cross over the actual boundary of
                          # the focal plane)

        self._band_to_int = {}
        self._band_to_int['u'] = 0
        self._band_to_int['g'] = 1
        self._band_to_int['r'] = 2
        self._band_to_int['i'] = 3
        self._band_to_int['z'] = 4
        self._band_to_int['y'] = 5

        self._int_to_band = 'ugrizy'

        self._n_grid = []
        self._m_grid = []

        # 2018 May 8
        # During development, I found that there was negligible
        # improvement in the fit when allowing n>4, so I am
        # hard-coding the limit of n=4 here.
        for n in range(4):
            for m in range(-n, n+1, 2):
                self._n_grid.append(n)
                self._m_grid.append(m)

        self._build_transformations()

    def _get_coeffs(self, x_in, y_in, x_out, y_out):
        """
        Get the coefficients of the best fit Zernike Polynomial
        expansion that transforms from x_in, y_in to x_out, y_out.

        Returns numpy arrays of the Zernike Polynomial expansion
        coefficients in x and y.  Zernike Polynomials correspond
        to the radial and angular orders stored in self._n_grid
        and self._m_grid.
        """

        polynomials = {}
        for n, m in zip(self._n_grid, self._m_grid):
            values = self._z_gen.evaluate_xy(x_in/self._rr, y_in/self._rr, n, m)
            polynomials[(n,m)] = values

        poly_keys = list(polynomials.keys())

        dx = x_out - x_in
        dy = y_out - y_in

        b = np.array([(dx*polynomials[k]).sum() for k in poly_keys])
        m = np.array([[(polynomials[k1]*polynomials[k2]).sum() for k1 in poly_keys]
                      for k2 in poly_keys])

        alpha_x_ = np.linalg.solve(m, b)
        alpha_x = {}
        for ii, kk in enumerate(poly_keys):
            alpha_x[kk] = alpha_x_[ii]


        b = np.array([(dy*polynomials[k]).sum() for k in poly_keys])
        m = np.array([[(polynomials[k1]*polynomials[k2]).sum() for k1 in poly_keys]
                      for k2 in poly_keys])

        alpha_y_ = np.linalg.solve(m, b)
        alpha_y = {}
        for ii, kk in enumerate(poly_keys):
            alpha_y[kk] = alpha_y_[ii]

        return alpha_x, alpha_y

    def _build_transformations(self):
        """
        Solve for and store the coefficients of the Zernike
        polynomial expansion of the difference between the
        naive and the bandpass-dependent optical distortions
        in the LSST camera.
        """
        catsim_dir = os.path.join(getPackageDir('sims_data'),
                                  'FocalPlaneData',
                                  'CatSimData')

        phosim_dir = os.path.join(getPackageDir('sims_data'),
                                  'FocalPlaneData',
                                  'PhoSimData')

        # the file which contains the input sky positions of the objects
        # that were given to PhoSim
        catsim_catalog = os.path.join(catsim_dir,'predicted_positions.txt')

        with open(catsim_catalog, 'r') as input_file:
            header = input_file.readline()
        params = header.strip().split()
        ra0 = np.radians(float(params[2]))
        dec0 = np.radians(float(params[4]))
        rotSkyPos = np.radians(float(params[6]))

        catsim_dtype = np.dtype([('id', int), ('xmm_old', float), ('ymm_old', float),
                                 ('xpup', float), ('ypup', float),
                                 ('raObs', float), ('decObs', float)])

        catsim_data = np.genfromtxt(catsim_catalog, dtype=catsim_dtype)

        sorted_dex = np.argsort(catsim_data['id'])
        catsim_data=catsim_data[sorted_dex]

        # convert from RA, Dec to pupil coors/FIELD_ANGLE
        x_field, y_field = _rawPupilCoordsFromObserved(np.radians(catsim_data['raObs']),
                                                       np.radians(catsim_data['decObs']),
                                                       ra0, dec0, rotSkyPos)

        # convert from FIELD_ANGLE to FOCAL_PLANE without attempting to model
        # the optical distortions in the telescope
        field_to_focal = self._camera.getTransform(FIELD_ANGLE, FOCAL_PLANE)
        catsim_xmm = np.zeros(len(x_field), dtype=float)
        catsim_ymm = np.zeros(len(y_field), dtype=float)
        for ii, (xx, yy) in enumerate(zip(x_field, y_field)):
            focal_pt = field_to_focal.applyForward(geom.Point2D(xx, yy))
            catsim_xmm[ii] = focal_pt.getX()
            catsim_ymm[ii] = focal_pt.getY()

        phosim_dtype = np.dtype([('id', int), ('phot', float),
                                 ('xpix', float), ('ypix', float)])

        self._pupil_to_focal = {}
        self._focal_to_pupil = {}

        for i_filter in range(6):
            self._pupil_to_focal[self._int_to_band[i_filter]] = {}
            self._focal_to_pupil[self._int_to_band[i_filter]] = {}
            phosim_xmm = np.zeros(len(catsim_data['ypup']), dtype=float)
            phosim_ymm = np.zeros(len(catsim_data['ypup']), dtype=float)

            for det in self._camera:
                if det.getType() != SCIENCE:
                    continue
                pixels_to_focal = det.getTransform(PIXELS, FOCAL_PLANE)
                det_name = det.getName()
                bbox = det.getBBox()
                det_name_m = det_name.replace(':','').replace(',','').replace(' ','_')

                # read in the actual pixel positions of the sources as realized
                # by PhoSim
                centroid_name = 'centroid_lsst_e_2_f%d_%s_E000.txt' % (i_filter, det_name_m)
                full_name = os.path.join(phosim_dir, centroid_name)
                phosim_data = np.genfromtxt(full_name, dtype=phosim_dtype, skip_header=1)

                # make sure that the data we are fitting to is not too close
                # to the edge of the detector
                assert phosim_data['xpix'].min() > bbox.getMinY() + 50.0
                assert phosim_data['xpix'].max() < bbox.getMaxY() - 50.0
                assert phosim_data['ypix'].min() > bbox.getMinX() + 50.0
                assert phosim_data['ypix'].max() < bbox.getMaxX() - 50.0

                xpix, ypix = self._pixel_transformer.dmPixFromCameraPix(phosim_data['xpix'],
                                                                        phosim_data['ypix'],
                                                                        det_name)
                xmm = np.zeros(len(xpix), dtype=float)
                ymm = np.zeros(len(ypix), dtype=float)
                for ii in range(len(xpix)):
                    focal_pt = pixels_to_focal.applyForward(geom.Point2D(xpix[ii], ypix[ii]))
                    xmm[ii] = focal_pt.getX()
                    ymm[ii] = focal_pt.getY()
                phosim_xmm[phosim_data['id']-1] = xmm
                phosim_ymm[phosim_data['id']-1] = ymm

            # solve for the coefficients of the Zernike expansions
            # necessary to model the optical transformations and go
            # from the naive focal plane positions (catsim_xmm, catsim_ymm)
            # to the PhoSim realized focal plane positions
            alpha_x, alpha_y = self._get_coeffs(catsim_xmm, catsim_ymm,
                                                phosim_xmm, phosim_ymm)

            self._pupil_to_focal[self._int_to_band[i_filter]]['x'] = alpha_x
            self._pupil_to_focal[self._int_to_band[i_filter]]['y'] = alpha_y

            # solve for the coefficients to the Zernike expansions
            # necessary to go back from the PhoSim realized focal plane
            # positions to the naive CatSim predicted focal plane
            # positions
            alpha_x, alpha_y = self._get_coeffs(phosim_xmm, phosim_ymm,
                                                catsim_xmm, catsim_ymm)

            self._focal_to_pupil[self._int_to_band[i_filter]]['x'] = alpha_x
            self._focal_to_pupil[self._int_to_band[i_filter]]['y'] = alpha_y

    def _apply_transformation(self, transformation_dict, xmm, ymm, band):
        """
        Parameters
        ----------
        tranformation_dict -- a dict containing the coefficients
        of the Zernike decomposition to be applied

        xmm -- the input x position in mm

        ymm -- the input y position in mm

        band -- the filter in which we are operating
        (can be either a string or an int; 0=u, 1=g, 2=r, etc.)

        Returns
        -------
        dx -- the x offset resulting from the transformation

        dy -- the y offset resulting from the transformation
        """
        if isinstance(band, int):
            band = self._int_to_band[band]

        if isinstance(xmm, numbers.Number):
            dx = 0.0
            dy = 0.0
        else:
            dx = np.zeros(len(xmm), dtype=float)
            dy = np.zeros(len(ymm), dtype=float)

        for kk in self._pupil_to_focal[band]['x']:
            values = self._z_gen.evaluate_xy(xmm/self._rr, ymm/self._rr, kk[0], kk[1])
            dx += transformation_dict[band]['x'][kk]*values
            dy += transformation_dict[band]['y'][kk]*values

        return dx, dy

    def dxdy(self, xmm, ymm, band):
        """
        Apply the transformation necessary when going from pupil
        coordinates to focal plane coordinates.

        The recipe to correctly use this method is

        xf0, yf0 = focalPlaneCoordsFromPupilCoords(xpupil, ypupil,
                                                   camera=lsst_camera())

        dx, dy = LsstZernikeFitter().dxdy(xf0, yf0, band=band)

        xf = xf0 + dx
        yf = yf0 + dy

        xf and yf are now the actual position in millimeters on the
        LSST focal plane corresponding to xpupil, ypupil

        Parameters
        ----------
        xmm -- the naive x focal plane position in mm

        ymm -- the naive y focal plane position in mm

        band -- the filter in which we are operating
        (can be either a string or an int; 0=u, 1=g, 2=r, etc.)

        Returns
        -------
        dx -- the offset in the x focal plane position in mm

        dy -- the offset in the y focal plane position in mm
        """
        return self._apply_transformation(self._pupil_to_focal, xmm, ymm, band)

    def dxdy_inverse(self, xmm, ymm, band):
        """
        Apply the transformation necessary when going from focal
        plane coordinates to pupil coordinates.

        The recipe to correctly use this method is

        dx, dy = LsstZernikeFitter().dxdy_inverse(xf, yf, band=band)

        xp, yp = pupilCoordsFromFocalPlaneCoords(xf+dx,
                                                 yf+dy,
                                                 camera=lsst_camera())

        xp and yp are now the actual position in radians on the pupil
        corresponding to the focal plane coordinates xf, yf

        Parameters
        ----------
        xmm -- the naive x focal plane position in mm

        ymm -- the naive y focal plane position in mm

        band -- the filter in which we are operating
        (can be either a string or an int; 0=u, 1=g, 2=r, etc.)

        Returns
        -------
        dx -- the offset in the x focal plane position in mm

        dy -- the offset in the y focal plane position in mm
        """
        return self._apply_transformation(self._focal_to_pupil, xmm, ymm, band)
Exemplo n.º 11
0
class LsstZernikeFitter(object):
    """
    This class will fit and then apply the Zernike polynomials needed
    to correct the FIELD_ANGLE to FOCAL_PLANE transformation for the
    filter-dependent part.
    """
    def __init__(self):
        self._camera = lsst_camera()
        self._pixel_transformer = DMtoCameraPixelTransformer()
        self._z_gen = ZernikePolynomialGenerator()

        self._rr = 500.0  # radius in mm of circle containing LSST focal plane;
        # make it a little bigger to accommodate any slop in
        # the conversion from focal plane coordinates back to
        # pupil coordinates (in case the optical distortions
        # cause points to cross over the actual boundary of
        # the focal plane)

        self._band_to_int = {}
        self._band_to_int['u'] = 0
        self._band_to_int['g'] = 1
        self._band_to_int['r'] = 2
        self._band_to_int['i'] = 3
        self._band_to_int['z'] = 4
        self._band_to_int['y'] = 5

        self._int_to_band = 'ugrizy'

        self._n_grid = []
        self._m_grid = []

        # 2018 May 8
        # During development, I found that there was negligible
        # improvement in the fit when allowing n>4, so I am
        # hard-coding the limit of n=4 here.
        for n in range(4):
            for m in range(-n, n + 1, 2):
                self._n_grid.append(n)
                self._m_grid.append(m)

        self._build_transformations()

    def _get_coeffs(self, x_in, y_in, x_out, y_out):
        """
        Get the coefficients of the best fit Zernike Polynomial
        expansion that transforms from x_in, y_in to x_out, y_out.

        Returns numpy arrays of the Zernike Polynomial expansion
        coefficients in x and y.  Zernike Polynomials correspond
        to the radial and angular orders stored in self._n_grid
        and self._m_grid.
        """

        polynomials = {}
        for n, m in zip(self._n_grid, self._m_grid):
            values = self._z_gen.evaluate_xy(x_in / self._rr, y_in / self._rr,
                                             n, m)
            polynomials[(n, m)] = values

        poly_keys = list(polynomials.keys())

        dx = x_out - x_in
        dy = y_out - y_in

        b = np.array([(dx * polynomials[k]).sum() for k in poly_keys])
        m = np.array([[(polynomials[k1] * polynomials[k2]).sum()
                       for k1 in poly_keys] for k2 in poly_keys])

        alpha_x_ = np.linalg.solve(m, b)
        alpha_x = {}
        for ii, kk in enumerate(poly_keys):
            alpha_x[kk] = alpha_x_[ii]

        b = np.array([(dy * polynomials[k]).sum() for k in poly_keys])
        m = np.array([[(polynomials[k1] * polynomials[k2]).sum()
                       for k1 in poly_keys] for k2 in poly_keys])

        alpha_y_ = np.linalg.solve(m, b)
        alpha_y = {}
        for ii, kk in enumerate(poly_keys):
            alpha_y[kk] = alpha_y_[ii]

        return alpha_x, alpha_y

    def _build_transformations(self):
        """
        Solve for and store the coefficients of the Zernike
        polynomial expansion of the difference between the
        naive and the bandpass-dependent optical distortions
        in the LSST camera.
        """
        catsim_dir = os.path.join(getPackageDir('sims_data'), 'FocalPlaneData',
                                  'CatSimData')

        phosim_dir = os.path.join(getPackageDir('sims_data'), 'FocalPlaneData',
                                  'PhoSimData')

        # the file which contains the input sky positions of the objects
        # that were given to PhoSim
        catsim_catalog = os.path.join(catsim_dir, 'predicted_positions.txt')

        with open(catsim_catalog, 'r') as input_file:
            header = input_file.readline()
        params = header.strip().split()
        ra0 = np.radians(float(params[2]))
        dec0 = np.radians(float(params[4]))
        rotSkyPos = np.radians(float(params[6]))

        catsim_dtype = np.dtype([('id', int), ('xmm_old', float),
                                 ('ymm_old', float), ('xpup', float),
                                 ('ypup', float), ('raObs', float),
                                 ('decObs', float)])

        catsim_data = np.genfromtxt(catsim_catalog, dtype=catsim_dtype)

        sorted_dex = np.argsort(catsim_data['id'])
        catsim_data = catsim_data[sorted_dex]

        # convert from RA, Dec to pupil coors/FIELD_ANGLE
        x_field, y_field = _rawPupilCoordsFromObserved(
            np.radians(catsim_data['raObs']),
            np.radians(catsim_data['decObs']), ra0, dec0, rotSkyPos)

        # convert from FIELD_ANGLE to FOCAL_PLANE without attempting to model
        # the optical distortions in the telescope
        field_to_focal = self._camera.getTransform(FIELD_ANGLE, FOCAL_PLANE)
        catsim_xmm = np.zeros(len(x_field), dtype=float)
        catsim_ymm = np.zeros(len(y_field), dtype=float)
        for ii, (xx, yy) in enumerate(zip(x_field, y_field)):
            focal_pt = field_to_focal.applyForward(geom.Point2D(xx, yy))
            catsim_xmm[ii] = focal_pt.getX()
            catsim_ymm[ii] = focal_pt.getY()

        phosim_dtype = np.dtype([('id', int), ('phot', float), ('xpix', float),
                                 ('ypix', float)])

        self._pupil_to_focal = {}
        self._focal_to_pupil = {}

        for i_filter in range(6):
            self._pupil_to_focal[self._int_to_band[i_filter]] = {}
            self._focal_to_pupil[self._int_to_band[i_filter]] = {}
            phosim_xmm = np.zeros(len(catsim_data['ypup']), dtype=float)
            phosim_ymm = np.zeros(len(catsim_data['ypup']), dtype=float)

            for det in self._camera:
                if det.getType() != SCIENCE:
                    continue
                pixels_to_focal = det.getTransform(PIXELS, FOCAL_PLANE)
                det_name = det.getName()
                bbox = det.getBBox()
                det_name_m = det_name.replace(':', '').replace(',',
                                                               '').replace(
                                                                   ' ', '_')

                # read in the actual pixel positions of the sources as realized
                # by PhoSim
                centroid_name = 'centroid_lsst_e_2_f%d_%s_E000.txt' % (
                    i_filter, det_name_m)
                full_name = os.path.join(phosim_dir, centroid_name)
                phosim_data = np.genfromtxt(full_name,
                                            dtype=phosim_dtype,
                                            skip_header=1)

                # make sure that the data we are fitting to is not too close
                # to the edge of the detector
                assert phosim_data['xpix'].min() > bbox.getMinY() + 50.0
                assert phosim_data['xpix'].max() < bbox.getMaxY() - 50.0
                assert phosim_data['ypix'].min() > bbox.getMinX() + 50.0
                assert phosim_data['ypix'].max() < bbox.getMaxX() - 50.0

                xpix, ypix = self._pixel_transformer.dmPixFromCameraPix(
                    phosim_data['xpix'], phosim_data['ypix'], det_name)
                xmm = np.zeros(len(xpix), dtype=float)
                ymm = np.zeros(len(ypix), dtype=float)
                for ii in range(len(xpix)):
                    focal_pt = pixels_to_focal.applyForward(
                        geom.Point2D(xpix[ii], ypix[ii]))
                    xmm[ii] = focal_pt.getX()
                    ymm[ii] = focal_pt.getY()
                phosim_xmm[phosim_data['id'] - 1] = xmm
                phosim_ymm[phosim_data['id'] - 1] = ymm

            # solve for the coefficients of the Zernike expansions
            # necessary to model the optical transformations and go
            # from the naive focal plane positions (catsim_xmm, catsim_ymm)
            # to the PhoSim realized focal plane positions
            alpha_x, alpha_y = self._get_coeffs(catsim_xmm, catsim_ymm,
                                                phosim_xmm, phosim_ymm)

            self._pupil_to_focal[self._int_to_band[i_filter]]['x'] = alpha_x
            self._pupil_to_focal[self._int_to_band[i_filter]]['y'] = alpha_y

            # solve for the coefficients to the Zernike expansions
            # necessary to go back from the PhoSim realized focal plane
            # positions to the naive CatSim predicted focal plane
            # positions
            alpha_x, alpha_y = self._get_coeffs(phosim_xmm, phosim_ymm,
                                                catsim_xmm, catsim_ymm)

            self._focal_to_pupil[self._int_to_band[i_filter]]['x'] = alpha_x
            self._focal_to_pupil[self._int_to_band[i_filter]]['y'] = alpha_y

    def _apply_transformation(self, transformation_dict, xmm, ymm, band):
        """
        Parameters
        ----------
        tranformation_dict -- a dict containing the coefficients
        of the Zernike decomposition to be applied

        xmm -- the input x position in mm

        ymm -- the input y position in mm

        band -- the filter in which we are operating
        (can be either a string or an int; 0=u, 1=g, 2=r, etc.)

        Returns
        -------
        dx -- the x offset resulting from the transformation

        dy -- the y offset resulting from the transformation
        """
        if isinstance(band, int):
            band = self._int_to_band[band]

        if isinstance(xmm, numbers.Number):
            dx = 0.0
            dy = 0.0
        else:
            dx = np.zeros(len(xmm), dtype=float)
            dy = np.zeros(len(ymm), dtype=float)

        for kk in self._pupil_to_focal[band]['x']:
            values = self._z_gen.evaluate_xy(xmm / self._rr, ymm / self._rr,
                                             kk[0], kk[1])
            dx += transformation_dict[band]['x'][kk] * values
            dy += transformation_dict[band]['y'][kk] * values

        return dx, dy

    def dxdy(self, xmm, ymm, band):
        """
        Apply the transformation necessary when going from pupil
        coordinates to focal plane coordinates.

        The recipe to correctly use this method is

        xf0, yf0 = focalPlaneCoordsFromPupilCoords(xpupil, ypupil,
                                                   camera=lsst_camera())

        dx, dy = LsstZernikeFitter().dxdy(xf0, yf0, band=band)

        xf = xf0 + dx
        yf = yf0 + dy

        xf and yf are now the actual position in millimeters on the
        LSST focal plane corresponding to xpupil, ypupil

        Parameters
        ----------
        xmm -- the naive x focal plane position in mm

        ymm -- the naive y focal plane position in mm

        band -- the filter in which we are operating
        (can be either a string or an int; 0=u, 1=g, 2=r, etc.)

        Returns
        -------
        dx -- the offset in the x focal plane position in mm

        dy -- the offset in the y focal plane position in mm
        """
        return self._apply_transformation(self._pupil_to_focal, xmm, ymm, band)

    def dxdy_inverse(self, xmm, ymm, band):
        """
        Apply the transformation necessary when going from focal
        plane coordinates to pupil coordinates.

        The recipe to correctly use this method is

        dx, dy = LsstZernikeFitter().dxdy_inverse(xf, yf, band=band)

        xp, yp = pupilCoordsFromFocalPlaneCoords(xf+dx,
                                                 yf+dy,
                                                 camera=lsst_camera())

        xp and yp are now the actual position in radians on the pupil
        corresponding to the focal plane coordinates xf, yf

        Parameters
        ----------
        xmm -- the naive x focal plane position in mm

        ymm -- the naive y focal plane position in mm

        band -- the filter in which we are operating
        (can be either a string or an int; 0=u, 1=g, 2=r, etc.)

        Returns
        -------
        dx -- the offset in the x focal plane position in mm

        dy -- the offset in the y focal plane position in mm
        """
        return self._apply_transformation(self._focal_to_pupil, xmm, ymm, band)
Exemplo n.º 12
0
from lsst.afw.cameraGeom import PIXELS, FOCAL_PLANE, SCIENCE
import lsst.afw.geom as afwGeom
from lsst.sims.GalSimInterface import LSSTCameraWrapper
from lsst.sims.utils import ZernikePolynomialGenerator

from lsst.sims.coordUtils import LsstZernikeFitter

import time

z_fitter = LsstZernikeFitter()
camera = LsstSimMapper().camera
camera_wrapper = LSSTCameraWrapper()

t_start = time.time()

z_gen = ZernikePolynomialGenerator()
n_grid = []
m_grid = []
for n in range(8):
    for m in range(-n, n+1, 2):
        n_grid.append(n)
        m_grid.append(m)

fig_dir = os.path.join('learning', 'figs')
catalog_dir = os.path.join('learning', 'catalogs3')
phosim_dir = os.path.join('learning', 'output3')
prediction_cat = os.path.join(catalog_dir, 'star_predicted_2.txt')
dtype = np.dtype([('id', int), ('xmm', float), ('ymm', float),
                  ('xpup', float), ('ypup', float),
                  ('raObs', float), ('decObs', float)])