def test_transforms_compound(tmpdir): tree = { 'compound': astmodels.Shift(1) & astmodels.Shift(2) | astmodels.Sky2Pix_TAN() | astmodels.Rotation2D() | astmodels.AffineTransformation2D([[2, 0], [0, 2]], [42, 32]) + astmodels.Rotation2D(32) } helpers.assert_roundtrip_tree(tree, tmpdir)
def fpa2asdf(fpafile, outname, ref_kw): """ Create an asdf reference file with the FPA description. The CDP2 delivery includes a fits file - "FPA.fpa" which is the input to this function. This file is converted to asdf and is a reference file of type "FPA". nirspec_fs_ref_tools.fpa2asdf('Ref_Files/CoordTransform/Description/FPA.fpa', 'fpa.asdf') Parameters ---------- fpafile : str A fits file with FPA description (FPA.fpa) outname : str Name of output ASDF file. """ with open(fpafile) as f: lines = [l.strip() for l in f.readlines()] # NRS1 ind = lines.index("*SCA491_PitchX") scalex_nrs1 = models.Scale(1 / float(lines[ind + 1]), name='fpa_scale_x') ind = lines.index("*SCA491_PitchY") scaley_nrs1 = models.Scale(1 / float(lines[ind + 1]), name='fpa_scale_y') ind = lines.index("*SCA491_RotAngle") rot_nrs1 = models.Rotation2D(np.rad2deg(-float(lines[ind + 1])), name='fpa_rotation') ind = lines.index("*SCA491_PosX") shiftx_nrs1 = models.Shift(-float(lines[ind + 1]), name='fpa_shift_x') ind = lines.index("*SCA491_PosY") shifty_nrs1 = models.Shift(-float(lines[ind + 1]), name='fpa_shift_y') # NRS2 ind = lines.index("*SCA492_PitchX") scalex_nrs2 = models.Scale(1 / float(lines[ind + 1]), name='fpa_scale_x') ind = lines.index("*SCA492_PitchY") scaley_nrs2 = models.Scale(1 / float(lines[ind + 1]), name='fpa_scale_y') ind = lines.index("*SCA492_RotAngle") rot_nrs2 = models.Rotation2D(np.rad2deg(float(lines[ind + 1])), name='fpa_rotation') ind = lines.index("*SCA492_PosX") shiftx_nrs2 = models.Shift(-float(lines[ind + 1]), name='fpa_shift_x') ind = lines.index("*SCA492_PosY") shifty_nrs2 = models.Shift(-float(lines[ind + 1]), name='fpa_shift_y') tree = ref_kw.copy() tree['NRS1'] = (shiftx_nrs1 & shifty_nrs1) | rot_nrs1 | (scalex_nrs1 & scaley_nrs1) tree['NRS2'] = (shiftx_nrs2 & shifty_nrs2) | rot_nrs2 | (scalex_nrs2 & scaley_nrs2) fasdf = AsdfFile() fasdf.tree = tree fasdf.write_to(outname) return fasdf
def test_inverse_transforms(tmpdir): rotation = astmodels.Rotation2D(32) rotation.inverse = astmodels.Rotation2D(45) real_rotation = astmodels.Rotation2D(32) tree = {'rotation': rotation, 'real_rotation': real_rotation} def check(ff): assert ff.tree['rotation'].inverse.angle == 45 helpers.assert_roundtrip_tree(tree, tmpdir, asdf_check_func=check)
def ifu_slicer2asdf(ifuslicer, author, description, useafter): """ Create an asdf reference file with the MSA description. ifu_slicer2asdf("IFU_slicer.sgd", "ifu_slicer.asdf") Parameters ---------- ifuslicer : str A fits file with the IFU slicer description outname : str Name of output ASDF file. """ f = fits.open(ifuslicer) data = f[1].data header = f[1].header shiftx = models.Shift(header['XREF'], name='ifuslicer_x') shifty = models.Shift(header['YREF'], name='ifuslicer_y') rot = models.Rotation2D(np.rad2deg(header['ROT']), name='ifuslicer_rotate') model = rot | shiftx & shifty f.close() slicer_model = IFUSlicerModel() slicer_model.model = model slicer_model.data = data slicer_model.meta.author = author slicer_model.meta.description = description slicer_model.meta.useafter = useafter slicer_model.meta.pedigree = "GROUND" return slicer_model
def ifu_slicer2asdf(ifuslicer, outname, ref_kw): """ Create an asdf reference file with the MSA description. ifu_slicer2asdf("IFU_slicer.sgd", "ifu_slicer.asdf") Parameters ---------- ifuslicer : str A fits file with the IFU slicer description outname : str Name of output ASDF file. """ f = fits.open(ifuslicer) tree = ref_kw.copy() data = f[1].data header = f[1].header shiftx = models.Shift(header['XREF'], name='ifu_slicer_xref') shifty = models.Shift(header['YREF'], name='ifu_slicer_yref') rot = models.Rotation2D(header['ROT'], name='ifu_slicer_rot') model = rot | shiftx & shifty tree['model'] = model tree['data'] = f[1].data f.close() fasdf = AsdfFile() fasdf.tree = tree fasdf.add_history_entry("Build 6") fasdf.write_to(outname) return fasdf
def pcf_forward(pcffile, outname): """ Create the **IDT** forward transform from collimator to gwa. """ with open(pcffile) as f: lines = [l.strip() for l in f.readlines()] factors = lines[lines.index('*Factor 2') + 1].split() # factor==1/factor in backward msa2ote direction and factor==factor in sky2detector direction scale = models.Scale(float(factors[0]), name="x_scale") & \ models.Scale(float(factors[1]), name="y_scale") rotation_angle = lines[lines.index('*Rotation') + 1] # The minius sign here is because astropy.modeling has the opposite direction of rotation than the idl implementation rotation = models.Rotation2D(-float(rotation_angle), name='rotation') # Here the model is called "output_shift" but in the team version it is the "input_shift". input_rot_center = lines[lines.index('*InputRotationCentre 2') + 1].split() input_rot_shift = models.Shift(-float(input_rot_center[0]), name='input_x_shift') & \ models.Shift(-float(input_rot_center[1]), name='input_y_shift') # Here the model is called "input_shift" but in the team version it is the "output_shift". output_rot_center = lines[lines.index('*OutputRotationCentre 2') + 1].split() output_rot_shift = models.Shift(float(output_rot_center[0]), name='output_x_shift') & \ models.Shift(float(output_rot_center[1]), name='output_y_shift') degree = int(lines[lines.index('*FitOrder') + 1]) xcoeff_index = lines.index('*xForwardCoefficients 21 2') xlines = lines[xcoeff_index + 1: xcoeff_index + 22] xcoeff_forward = coeffs_from_pcf(degree, xlines) x_poly_forward = models.Polynomial2D(degree, name='x_poly_forward', **xcoeff_forward) ycoeff_index = lines.index('*yForwardCoefficients 21 2') ycoeff_forward = coeffs_from_pcf(degree, lines[ycoeff_index + 1: ycoeff_index + 22]) y_poly_forward = models.Polynomial2D(degree, name='y_poly_forward', **ycoeff_forward) xcoeff_index = lines.index('*xBackwardCoefficients 21 2') xcoeff_backward = coeffs_from_pcf(degree, lines[xcoeff_index + 1: xcoeff_index + 22]) x_poly_backward = models.Polynomial2D(degree, name='x_poly_backward', **xcoeff_backward) ycoeff_index = lines.index('*yBackwardCoefficients 21 2') ycoeff_backward = coeffs_from_pcf(degree, lines[ycoeff_index + 1: ycoeff_index + 22]) y_poly_backward = models.Polynomial2D(degree, name='y_poly_backward', **ycoeff_backward) x_poly_forward.inverse = x_poly_backward y_poly_forward.inverse = y_poly_backward poly_mapping1 = Mapping((0, 1, 0, 1)) poly_mapping1.inverse = Identity(2) poly_mapping2 = Identity(2) poly_mapping2.inverse = Mapping((0, 1, 0, 1)) model = input_rot_shift | rotation | scale | output_rot_shift | \ poly_mapping1 | x_poly_forward & y_poly_forward | poly_mapping2 f = AsdfFile() f.tree = {'model': model} f.write_to(outname)
def test_fix_inputs(tmpdir): model = astmodels.Pix2Sky_TAN() | astmodels.Rotation2D() tree = { 'compound': fix_inputs(model, {'x': 45}), 'compound1': fix_inputs(model, {0: 45}) } helpers.assert_roundtrip_tree(tree, tmpdir)
def test_compound_deepcopy(): model = (models.Gaussian1D(10, 2, 3) | models.Shift(2)) & models.Rotation2D(21.3) new_model = model.deepcopy() assert id(model) != id(new_model) assert id(model._leaflist) != id(new_model._leaflist) assert id(model[0]) != id(new_model[0]) assert id(model[1]) != id(new_model[1]) assert id(model[2]) != id(new_model[2])
def test_domain(tmpdir): def check(ff): assert ff.tree['rot'].meta['domain'] == { 'lower': 0, 'upper': 1, 'includes_lower': True} model = astmodels.Rotation2D(23) model.meta['domain'] = {'lower': 0, 'upper': 1, 'includes_lower': True} tree = {'rot': model} helpers.assert_roundtrip_tree(tree, tmpdir, check)
def test_calculate_affine_matrices(angle, scale, xoffset, yoffset): m = ((models.Scale(scale) & models.Scale(scale)) | models.Rotation2D(angle) | (models.Shift(xoffset) & models.Shift(yoffset))) affine = adwcs.calculate_affine_matrices(m, (100, 100)) assert_allclose(affine.offset, (yoffset, xoffset), atol=1e-10) angle = math.radians(angle) assert_allclose(affine.matrix, ((scale * math.cos(angle), scale * math.sin(angle)), (-scale * math.sin(angle), scale * math.cos(angle))), atol=1e-10)
def test_LabelMapperRange(): sel = create_range_mapper() assert(sel(6, 2) == 4.2) with pytest.raises(TypeError): selector.LabelMapperRange(('x', 'y'), mapper={(1, 5): models.Rotation2D(23), (7, 10): models.Shift(1) } )
def _create_wcs_from_offsets(adinput, adref, center_of_rotation=None): """ This function uses the POFFSET, QOFFSET, and PA header keywords to create a new WCS for an image. Its primary role is for GNIRS. For ease, it works out the (RA,DEC) of the centre of rotation in the reference image and determines where in the input image this is. Parameters ---------- adinput: AstroData The input image whose WCS needs to be rewritten adreference: AstroData The reference image with a trustworthy WCS center_of_rotation: 2-tuple Location of rotation center (x, y) """ log = logutils.get_logger(__name__) if len(adinput) != len(adref): log.warning("Number of extensions in input files are different. " "Cannot correct WCS.") return adinput log.stdinfo("Updating WCS of {} based on {}".format(adinput.filename, adref.filename)) try: xdiff = adref.detector_x_offset() - adinput.detector_x_offset() ydiff = adref.detector_y_offset() - adinput.detector_y_offset() pa1 = adref.phu['PA'] pa2 = adinput.phu['PA'] except (KeyError, TypeError): # TypeError if offset is None log.warning("Cannot obtain necessary offsets from headers " "so no change will be made") return adinput # We expect mosaicked inputs but there's no reason why this couldn't # work for all extensions in an image for extin, extref in zip(adinput, adref): # Will need to have some sort of LUT here eventually. But for now... if center_of_rotation is None: center_of_rotation = (630.0, 520.0) if 'GNIRS' in adref.tags \ else tuple(0.5*(x-1) for x in extref.data.shape[::-1]) wcsref = WCS(extref.hdr) ra0, dec0 = wcsref.all_pix2world(center_of_rotation[0], center_of_rotation[1], 1) extin.hdr['CRVAL1'] = float(ra0) extin.hdr['CRVAL2'] = float(dec0) extin.hdr['CRPIX1'] = center_of_rotation[0] - xdiff extin.hdr['CRPIX2'] = center_of_rotation[1] - ydiff cd = models.Rotation2D(angle=pa1-pa2)(*wcsref.wcs.cd) extin.hdr['CD1_1'] = cd[0][0] extin.hdr['CD1_2'] = cd[0][1] extin.hdr['CD2_1'] = cd[1][0] extin.hdr['CD2_2'] = cd[1][1] return adinput
def test_LabelMapperDict(): dmapper = create_scalar_mapper() sel = selector.LabelMapperDict(('x', 'y'), dmapper, atol=10**-3, inputs_mapping=models.Mapping((0,), n_inputs=2)) assert(sel(-1.9580, 2) == dmapper[-1.95805483](-1.95805483, 2)) with pytest.raises(TypeError): selector.LabelMapperDict(('x', 'y'), mapper={1: models.Rotation2D(23), 2: models.Shift(1) } )
def _apply_model_to_wcs(adinput, transform=None, keyword_comments=None): """ This function modifies the WCS of input images according to astropy Models that describe how to map the input image pixels to their correct location, i.e., an input pixel (x,y) should have the world coordinates WCS(m(x,y)), where m is the transformation model. Parameters ---------- adinput: list of AD objects input images for WCS to be modified transform: list of Models transformations (in pixel space) keyword_comments: dict the comment for each FITS keyword Returns ------- list of ADs: modified AD instances """ if len(transform) != len(adinput): raise IOError("List of models have the same number of " "elements as adinput") for ad, model in zip(adinput, transform): crpix1 = ad.hdr['CRPIX1'][0] crpix2 = ad.hdr['CRPIX2'][0] newcrpix = model.inverse(crpix1, crpix2) # Determine total magnification and rotation from all relevant # model components magnification = np.multiply.reduce([ getattr(model, p).value for p in model.param_names if 'factor' in p ]) rotation = np.add.reduce([ getattr(model, p).value for p in model.param_names if 'angle' in p ]) cdmatrix = [[ ad.hdr['CD{}_{}'.format(i, j)][0] / magnification for j in [1, 2] ] for i in [1, 2]] m = models.Rotation2D(-rotation) newcdmatrix = m(*cdmatrix) for ax in 1, 2: ad.hdr.set('CRPIX{}'.format(ax), newcrpix[ax - 1], comment=keyword_comments["CRPIX{}".format(ax)]) for ax2 in 1, 2: ad.hdr.set('CD{}_{}'.format(ax, ax2), newcdmatrix[ax - 1][ax2 - 1], comment=keyword_comments['CD{}_{}'.format(ax, ax2)]) return adinput
def msa2asdf(msafile, author, description, useafter): """ Create an asdf reference file with the MSA description. mas2asfdf("MSA.msa", "msa.asdf") Parameters ---------- msafile : str A fits file with MSA description (MSA.msa) outname : str Name of output ASDF file. """ f = fits.open(msafile) data = f[5].data # SLITS and IFU header = f[5].header shiftx = models.Shift(header['SLITXREF'], name='msa_slit_x') shifty = models.Shift(header['SLITYREF'], name='msa_slit_y') slitrot = models.Rotation2D(np.rad2deg(header['SLITROT']), name='msa_slit_rot') msa_model = MSAModel() msa_model.Q5.model = slitrot | shiftx & shifty msa_model.Q5.data = f[5].data for i in range(1, 5): header = f[i].header shiftx = models.Shift(header['QUADXREF'], name='msa_Q{0}_x'.format(i)) shifty = models.Shift(header['QUADYREF'], name='msa_Q{0}_y'.format(i)) slitrot = models.Rotation2D(np.rad2deg(header['QUADROT']), name='msa_Q{0}_rot'.format(i)) model = slitrot | shiftx & shifty data = f[i].data name = "Q{0}".format(i) setattr(msa_model, name, {'model': model, 'data': data}) f.close() msa_model.meta.author = author msa_model.meta.description = description msa_model.meta.useafter = useafter msa_model.meta.pedigree = 'GROUND' return msa_model
def msa2asdf(msafile, outname, ref_kw): """ Create an asdf reference file with the MSA description. mas2asfdf("MSA.msa", "msa.asdf") Parameters ---------- msafile : str A fits file with MSA description (MSA.msa) outname : str Name of output ASDF file. """ f = fits.open(msafile) tree = ref_kw.copy() data = f[5].data # SLITS and IFU header = f[5].header shiftx = models.Shift(header['SLITXREF'], name='slit_xref') shifty = models.Shift(header['SLITYREF'], name='slit_yref') slitrot = models.Rotation2D(header['SLITROT'], name='slit_rot') tree[5] = {} tree[5]['model'] = slitrot | shiftx & shifty tree[5]['data'] = f[5].data for i in range(1, 5): header = f[i].header shiftx = models.Shift(header['QUADXREF'], name='msa_xref') shifty = models.Shift(header['QUADYREF'], name='msa_yref') slitrot = models.Rotation2D(header['QUADROT'], name='msa_rot') tree[i] = {} tree[i]['model'] = slitrot | shiftx & shifty tree[i]['data'] = f[i].data f.close() fasdf = AsdfFile() fasdf.tree = tree fasdf.add_history_entry("Build 6") fasdf.write_to(outname) return fasdf
def test_fix_inputs(tmpdir): with warnings.catch_warnings(): # Some schema files are missing from asdf<=2.4.2 which causes warnings if LooseVersion(asdf.__version__) <= '2.4.2': warnings.filterwarnings('ignore', 'Unable to locate schema file') model = astmodels.Pix2Sky_TAN() | astmodels.Rotation2D() tree = { 'compound': fix_inputs(model, {'x': 45}), 'compound1': fix_inputs(model, {0: 45}) } helpers.assert_roundtrip_tree(tree, tmpdir)
def lrs(input_model, reference_files): """ Create the WCS pipeline for a MIRI fixed slit observation. reference_files = {"specwcs": 'MIRI_FM_MIRIMAGE_P750L_DISTORTION_04.02.00.fits'} """ detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) focal_spatial = cf.Frame2D(name='focal', axes_order=(0, 1), unit=(u.arcmin, u.arcmin)) sky = cf.CelestialFrame(reference_frame=coord.ICRS()) spec = cf.SpectralFrame(name='wavelength', axes_order=(2, ), unit=(u.micron, ), axes_names=('lambda', )) focal = cf.CompositeFrame([focal_spatial, spec]) ref = fits.open(reference_files['specwcs']) ldata = ref[1].data if input_model.meta.exposure.type.lower() == 'mir_lrs-fixedslit': zero_point = ref[1].header['imx'], ref[1].header['imy'] elif input_model.meta.exposure.type.lower() == 'mir_lrs-slitless': #zero_point = ref[1].header['imysltl'], ref[1].header['imxsltl'] #zero point in reference file is wrong # This should eb moved eventually to the reference file. zero_point = [35, 442] #[35, 763] # account for subarray lrsdata = np.array([l for l in ldata]) x0 = lrsdata[:, 3] x1 = lrsdata[:, 5] y0 = lrsdata[:, 4] domain = [{ 'lower': x0.min() + zero_point[0], 'upper': x1.max() + zero_point[0] }, { 'lower': (y0.min() + zero_point[1]), 'upper': (y0.max() + zero_point[1]) }] log.info("Setting domain to {0}".format(domain)) lrs_wav_model = jwmodels.LRSWavelength(lrsdata, zero_point) ref.close() angle = np.arctan(0.00421924) spatial = models.Rotation2D(angle) det2focal = models.Mapping((0, 1, 0, 1)) | spatial & lrs_wav_model det2focal.meta['domain'] = domain pipeline = [(detector, det2focal), (focal, None)] #(sky, None) #] return pipeline
def wcs(self): """Return the WCS modified by the translation/scaling/rotation""" wcs = self._wcs.deepcopy() x_offset = self.x_offset.value y_offset = self.y_offset.value angle = self.angle.value factor = self.factor.value wcs.wcs.crpix += np.array([x_offset, y_offset]) if factor != 1: wcs.wcs.cd *= factor if angle != 0.0: m = models.Rotation2D(angle) wcs.wcs.cd = m(*wcs.wcs.cd) return wcs
def test_Gaussian2DRotation(): amplitude = 42 x_mean, y_mean = 0, 0 x_stddev, y_stddev = 2, 3 theta = Angle(10, 'deg') pars = dict(amplitude=amplitude, x_mean=x_mean, y_mean=y_mean, x_stddev=x_stddev, y_stddev=y_stddev) rotation = models.Rotation2D(angle=theta.degree) point1 = (x_mean + 2 * x_stddev, y_mean + 2 * y_stddev) point2 = rotation(*point1) g1 = models.Gaussian2D(theta=0, **pars) g2 = models.Gaussian2D(theta=theta.radian, **pars) value1 = g1(*point1) value2 = g2(*point2) assert_allclose(value1, value2)
def rotate(self, angle): """ Modify the CD matrices of all the headers to effect a rotation. This assumes that the CRVALi keywords are the same in all headers or else this modification may be inconsistent. Parameters ---------- angle: float Rotation angle (degrees) """ for ext in self: cd_matrix = models.Rotation2D(angle)(*WCS(ext.hdr).wcs.cd) ext.hdr.update({ 'CD{}_{}'.format(i + 1, j + 1): cd_matrix[i][j] for i in (0, 1) for j in (0, 1) }) ext.wcs = adwcs.fitswcs_to_gwcs(ext.hdr) self.phu['PA'] = (self.phu.get('PA', 0) + angle) % 360
def test_fix_inputs(tmpdir): with warnings.catch_warnings(): # Some schema files are missing from asdf<=2.4.2 which causes warnings if Version(asdf.__version__) <= Version('2.5.1'): warnings.filterwarnings('ignore', 'Unable to locate schema file') model0 = astmodels.Pix2Sky_TAN() model0.input_units_equivalencies = {'x': u.dimensionless_angles(), 'y': u.dimensionless_angles()} model1 = astmodels.Rotation2D() model = model0 | model1 tree = { 'compound': fix_inputs(model, {'x': 45}), 'compound1': fix_inputs(model, {0: 45}) } helpers.assert_roundtrip_tree(tree, tmpdir)
def test_Ellipse2D(): """Test Ellipse2D model.""" amplitude = 7.5 x0, y0 = 15, 15 theta = Angle(45, 'deg') em = models.Ellipse2D(amplitude, x0, y0, 7, 3, theta.radian) y, x = np.mgrid[0:30, 0:30] e = em(x, y) assert np.all(e[e > 0] == amplitude) assert e[y0, x0] == amplitude rotation = models.Rotation2D(angle=theta.degree) point1 = [2, 0] # Rotation2D center is (0, 0) point2 = rotation(*point1) point1 = np.array(point1) + [x0, y0] point2 = np.array(point2) + [x0, y0] e1 = models.Ellipse2D(amplitude, x0, y0, 7, 3, theta=0.) e2 = models.Ellipse2D(amplitude, x0, y0, 7, 3, theta=theta.radian) assert e1(*point1) == e2(*point2)
def msa2asdf(msafile, outname, ref_kw): """ Create an asdf reference file with the MSA description. The CDP2 delivery includes a fits file - "MSA.msa". This file is converted to asdf and serves as a reference file of type "REGIONS". mas2asfdf("MSA.msa", "msa.asdf") --> creates an 85MB file Parameters ---------- msafile : str A fits file with MSA description (MSA.msa) outname : str Name of output ASDF file. """ f = fits.open(msafile) tree = ref_kw.copy() data = f[5].data # SLITS and IFU header = f[5].header shiftx = models.Shift(header['SLITXREF'], name='slit_xref') shifty = models.Shift(header['SLITYREF'], name='slit_yref') slitrot = models.Rotation2D(header['SLITROT'], name='slit_rot') for j, slit in enumerate( ['S200A1', 'S200A2', 'S400A1', 'S1600A1', 'S200B1', 'IFU']): slitdata = data[j] t = {} for i, s in enumerate(['xcenter', 'ycenter', 'xsize', 'ysize']): t[s] = slitdata[i + 1] model = models.Scale(t['xsize']) & models.Scale(t['ysize']) | \ models.Shift(t['xcenter']) & models.Shift(t['ycenter']) | \ slitrot | shiftx & shifty t['model'] = model tree[slit] = t f.close() fasdf = AsdfFile() fasdf.tree = tree fasdf.write_to(outname) return fasdf
def test_Rotation2D_errors(): model = models.Rotation2D(angle=90 * u.deg) # Bad evaluation input shapes x = np.array([1, 2]) y = np.array([1, 2, 3]) message = "Expected input arrays to have the same shape" with pytest.raises(ValueError) as err: model.evaluate(x, y, model.angle) assert str(err.value) == message with pytest.raises(ValueError) as err: model.evaluate(y, x, model.angle) assert str(err.value) == message # Bad evaluation units x = np.array([1, 2]) y = np.array([1, 2]) message = "x and y must have compatible units" with pytest.raises(u.UnitsError) as err: model.evaluate(x * u.m, y, model.angle) assert str(err.value) == message
def add_galaxy(self, amplitude=None, n=4.0, r_e=1.0, axis_ratio=1.0, pa=0.0, x=None, y=None): """ Adds a Sersic profile galaxy, convolved with the seeing, at the specified location. Parameters ---------- amplitude: float Peak pixel value n: float Sersic index r_e: float effective radius (arcseconds) axis_ratio: float ratio of major to minor axis pa: float position angle of major axis x, y: float [0-indexed] location of centre of star in pixels (Decorated by @convert_rd2xy so ra, dec can be specified) """ obj = ((models.Shift(-x) & models.Shift(-y)) | models.Rotation2D(self.phu.get('PA', 0) - pa) | (models.Scale(axis_ratio) & models.Identity(1)) | Sersic( amplitude=amplitude, r_e=r_e / self.pixel_scale(), n=n)) ygrid, xgrid = np.mgrid[:self.data.shape[-2], :self.data.shape[-1]] obj_data = obj(xgrid, ygrid) sigma = 0.42466 * self.seeing / self.pixel_scale() convolved_data = gaussian_filter(obj_data, sigma=sigma, mode='constant') self.add(convolved_data)
def test_adjust_wcs_to_reference(astrofaker, no_wcs, rotate, scale): scale = False mods = [models.Identity(2)] for params in MODEL_PARMS: m = models.Shift(params[0]) & models.Shift(params[1]) if rotate: m |= models.Rotation2D(params[2]) if scale: m |= models.Scale(params[3]) & models.Scale(params[3]) mods.append(m) adinputs = make_images(astrofaker, mods=mods) if no_wcs: for ad in adinputs: del ad[0].hdr['CD*'] ad[0].wcs = None p = NIRIImage(adinputs) p.detectSources() p.adjustWCSToReference(first_pass=45 if no_wcs else 5, rotate=rotate, scale=scale) # We're going to confirm that a grid of input points are transformed # correctly, rather than comparing the components of the registration # model with the original model yin, xin = np.mgrid[:1000:100, :1000:100] for i, (ad, m) in enumerate(zip(p.streams['main'], mods)): if i == 0: # the Identity model: nothing to check continue m_regist = ad[0].wcs.forward_transform[:m.n_submodels] print(m_regist) xref, yref = m(xin, yin) xout, yout = m_regist(xin, yin) np.testing.assert_allclose(xref, xout, atol=0.1) np.testing.assert_allclose(yref, yout, atol=0.1) rms = np.sqrt(((xref - xout)**2 + (yref - yout)**2).mean()) assert rms < 0.05
def test_name(tmpdir): def check(ff): assert ff.tree['rot'].name == 'foo' tree = {'rot': astmodels.Rotation2D(23, name='foo')} helpers.assert_roundtrip_tree(tree, tmpdir, asdf_check_func=check)
def mosaicDetectors(self, adinputs=None, **params): """ This primitive does a full mosaic of all the arrays in an AD object. An appropriate geometry_conf.py module containing geometric information is required. Parameters ---------- suffix: str suffix to be added to output files. sci_only: bool mosaic only SCI image data. Default is False order: int (1-5) order of spline interpolation """ log = self.log log.debug(gt.log_message("primitive", self.myself(), "starting")) timestamp_key = self.timestamp_keys[self.myself()] suffix = params['suffix'] order = params['order'] attributes = ['data'] if params['sci_only'] else None geotable = import_module('.geometry_conf', self.inst_lookups) adoutputs = [] for ad in adinputs: if ad.phu.get(timestamp_key): log.warning("No changes will be made to {}, since it has " "already been processed by mosaicDetectors".format( ad.filename)) adoutputs.append(ad) continue if len(ad) == 1: log.warning("{} has only one extension, so there's nothing " "to mosaic".format(ad.filename)) adoutputs.append(ad) continue # If there's an overscan section, we must trim it before mosaicking try: overscan_kw = ad._keyword_for('overscan_section') except AttributeError: # doesn't exist for this AD, so carry on pass else: if overscan_kw in ad.hdr: ad = gt.trim_to_data_section(ad, self.keyword_comments) # Create the blocks (individual physical detectors) array_info = gt.array_information(ad) blocks = [ Block(ad[arrays], shape=shape) for arrays, shape in zip( array_info.extensions, array_info.array_shapes) ] offsets = [ ad[exts[0]].array_section() for exts in array_info.extensions ] detname = ad.detector_name() xbin, ybin = ad.detector_x_bin(), ad.detector_y_bin() geometry = geotable.geometry[detname] default_shape = geometry.get('default_shape') adg = AstroDataGroup() for block, origin, offset in zip(blocks, array_info.origins, offsets): # Origins are in (x, y) order in LUT block_geom = geometry[origin[::-1]] nx, ny = block_geom.get('shape', default_shape) nx /= xbin ny /= ybin shift = block_geom.get('shift', (0, 0)) rot = block_geom.get('rotation', 0.) mag = block_geom.get('magnification', (1, 1)) transform = Transform() # Shift the Block's coordinates based on its location within # the full array, to ensure any rotation takes place around # the true centre. if offset.x1 != 0 or offset.y1 != 0: transform.append( models.Shift(float(offset.x1) / xbin) & models.Shift(float(offset.y1) / ybin)) if rot != 0 or mag != (1, 1): # Shift to centre, do whatever, and then shift back transform.append( models.Shift(-0.5 * (nx - 1)) & models.Shift(-0.5 * (ny - 1))) if rot != 0: # Cope with non-square pixels by scaling in one # direction to make them square before applying the # rotation, and then reversing that. if xbin != ybin: transform.append( models.Identity(1) & models.Scale(ybin / xbin)) transform.append(models.Rotation2D(rot)) if xbin != ybin: transform.append( models.Identity(1) & models.Scale(xbin / ybin)) if mag != (1, 1): transform.append( models.Scale(mag[0]) & models.Scale(mag[1])) transform.append( models.Shift(0.5 * (nx - 1)) & models.Shift(0.5 * (ny - 1))) transform.append( models.Shift(float(shift[0]) / xbin) & models.Shift(float(shift[1]) / ybin)) adg.append(block, transform) adg.set_reference() ad_out = adg.transform(attributes=attributes, order=order, process_objcat=False) ad_out.orig_filename = ad.filename gt.mark_history(ad_out, primname=self.myself(), keyword=timestamp_key) ad_out.update_filename(suffix=suffix, strip=True) adoutputs.append(ad_out) return adoutputs
with pytest.raises(ValueError): model(1, 2, 3) with pytest.raises(ValueError): model(1) with pytest.raises(ValueError): model(1, 2, t=12, r=3) @pytest.mark.parametrize('model', [ models.Gaussian1D(), models.Polynomial1D(1, ), models.Tabular1D(lookup_table=np.ones((5, ))), models.Rotation2D(), models.Pix2Sky_TAN() ]) @pytest.mark.skipif('not HAS_SCIPY') def test_call_keyword_args_1(model): """ Test calling a model with positional, keywrd and a mixture of both arguments. """ positional = model(1) assert_allclose(positional, model(x=1)) model.inputs = ('r', ) assert_allclose(positional, model(r=1)) with pytest.raises(ValueError): model(1, 2, 3)