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 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)
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))
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)
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)
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)
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 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)
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]))
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)
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)
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)])