def oteip_to_v23(reference_files): """ Transform from the OTEIP frame to the V2V3 frame. Parameters ---------- reference_files: dict Dictionary with reference files returned by CRDS. Returns ------- model : `~astropy.modeling.core.Model` model. Transform from OTEIP to V2V3. """ with AsdfFile.open(reference_files['ote']) as f: ote = f.tree['model'].copy() fore2ote_mapping = Identity(3, name='fore2ote_mapping') fore2ote_mapping.inverse = Mapping((0, 1, 2, 2)) # Create the transform to v2/v3/lambda. The wavelength units up to this point are # meters as required by the pipeline but the desired output wavelength units is microns. # So we are going to Scale the spectral units by 1e6 (meters -> microns) # The spatial units are currently in deg. Convertin to arcsec. oteip_to_xyan = fore2ote_mapping | (ote & Scale(1e6)) # Add a shift for the aperture. oteip2v23 = oteip_to_xyan | Identity(1) & (Shift(468 / 3600) | Scale(-1)) & Identity(1) return oteip2v23
def compute_spec_transform(fiducial, refwcs): """ Compute a simple transform given a fidicial point in a spatial-spectral wcs. """ cdelt1 = refwcs.wcsinfo.cdelt1 / 3600. cdelt2 = refwcs.wcsinfo.cdelt2 / 3600. cdelt3 = refwcs.wcsinfo.cdelt3 roll_ref = refwcs.wcsinfo.roll_ref y, x = grid_from_spec_domain(refwcs) ra, dec, lam = refwcs(x, y) min_lam = np.nanmin(lam) offset = Shift(0.) & Shift(0.) rot = Rotation2D(roll_ref) scale = Scale(cdelt1) & Scale(cdelt2) tan = Pix2Sky_TAN() skyrot = RotateNative2Celestial(fiducial[0][0], fiducial[0][1], 180.0) spatial = offset | rot | scale | tan | skyrot spectral = Scale(cdelt3) | Shift(min_lam) mapping = Mapping((1, 1, 0), ) mapping.inverse = Mapping((2, 1)) transform = mapping | spatial & spectral transform.outputs = ('ra', 'dec', 'lamda') return transform
def run_test(model): wcsobj = model.meta.wcs for ch in model.meta.instrument.channel: ref_data = mrs_ref_data[ch + band_mapping[model.meta.instrument.band]] detector_to_alpha_beta = wcsobj.get_transform('detector', 'alpha_beta') #ab_to_xan_yan = wcsobj.get_transform('alpha_beta', 'Xan_Yan').set_input(int(ch)) ab_to_v2v3 = wcsobj.get_transform('alpha_beta', 'v2v3').set_input(int(ch)) ab_to_xan_yan = ab_to_v2v3 | Scale(1 / 60) & Scale(1 / 60) & Identity( 1) | Identity(1) & (Scale(-1) | Shift(-7.8)) & Identity(1) ref_alpha = ref_data['alpha'] ref_beta = ref_data['beta'] ref_lam = ref_data['lam'] x, y = ref_data['x'], ref_data['y'] for i, s in enumerate(ref_data['s']): sl = int(ch) * 100 + s alpha, beta, lam = detector_to_alpha_beta.set_input(sl)(x[i], y[i]) utils.assert_allclose(alpha, ref_alpha[i], atol=10**-4) utils.assert_allclose(beta, ref_beta[i], atol=10**-4) utils.assert_allclose(lam, ref_lam[i], atol=10**-4) xan, yan, lam = ab_to_xan_yan(ref_alpha, ref_beta, ref_lam) utils.assert_allclose(xan, ref_data['v2'], atol=10**-4) utils.assert_allclose(yan, ref_data['v3'], atol=10**-4) utils.assert_allclose(lam, ref_data['lam'], atol=10**-4)
def compute_output_transform(refwcs, filename, fiducial): """Compute a simple FITS-type WCS transform """ x0, y0 = refwcs.backward_transform(*fiducial) x1 = x0 + 1 y1 = y0 + 1 ra0, dec0 = refwcs(x0, y0) ra_xdir, dec_xdir = refwcs(x1, y0) ra_ydir, dec_ydir = refwcs(x0, y1) position0 = SkyCoord(ra=ra0, dec=dec0, unit='deg') position_xdir = SkyCoord(ra=ra_xdir, dec=dec_xdir, unit='deg') position_ydir = SkyCoord(ra=ra_ydir, dec=dec_ydir, unit='deg') offset_xdir = position0.spherical_offsets_to(position_xdir) offset_ydir = position0.spherical_offsets_to(position_ydir) xscale = np.abs(position0.separation(position_xdir).value) yscale = np.abs(position0.separation(position_ydir).value) scale = np.sqrt(xscale * yscale) c00 = offset_xdir[0].value / scale c01 = offset_xdir[1].value / scale c10 = offset_ydir[0].value / scale c11 = offset_ydir[1].value / scale pc_matrix = AffineTransformation2D(matrix=[[c00, c01], [c10, c11]]) cdelt = Scale(scale) & Scale(scale) return pc_matrix | cdelt
def test_slicing_on_instances_3(): """ Like `test_slicing_on_instances_2` but uses a compound model that does not have any invalid slices due to the resulting model being invalid (originally test_slicing_on_instances_2 passed without any ModelDefinitionErrors being raised, but that was before we prevented invalid models from being created). """ model_a = Shift(1, name='a') model_b = Shift(2, name='b') model_c = Gaussian1D(3, 0, 0.1, name='c') model_d = Scale(2, name='d') model_e = Scale(3, name='e') m = (model_a + model_b) | model_c | (model_d + model_e) assert m[1:].submodel_names == ('b', 'c', 'd', 'e') assert m[:].submodel_names == ('a', 'b', 'c', 'd', 'e') assert m['a':].submodel_names == ('a', 'b', 'c', 'd', 'e') assert m['c':'d'].submodel_names == ('c', 'd') assert m[1:2].name == 'b' assert m[2:7].submodel_names == ('c', 'd', 'e') with pytest.raises(IndexError): m['x'] with pytest.raises(IndexError): m['a':'r'] assert m[-4:4].submodel_names == ('b', 'c', 'd') assert m[-4:-2].submodel_names == ('b', 'c')
def test_basic_compound_inverse(): """ Test basic inversion of compound models in the limited sense supported for models made from compositions and joins only. """ t = (Shift(2) & Shift(3)) | (Scale(2) & Scale(3)) | Rotation2D(90) assert_allclose(t.inverse(*t(0, 1)), (0, 1))
def dva_corr_model(va_scale, v2_ref, v3_ref): """ Create transformation that accounts for differential velocity aberration (scale). Parameters ---------- va_scale : float, None Ratio of the apparent plate scale to the true plate scale. When ``va_scale`` is `None`, it is assumed to be identical to ``1`` and an ``astropy.modeling.models.Identity`` model will be returned. v2_ref : float, None Telescope ``v2`` coordinate of the reference point in ``arcsec``. When ``v2_ref`` is `None`, it is assumed to be identical to ``0``. v3_ref : float, None Telescope ``v3`` coordinate of the reference point in ``arcsec``. When ``v3_ref`` is `None`, it is assumed to be identical to ``0``. Returns ------- va_corr : astropy.modeling.CompoundModel, astropy.modeling.models.Identity A 2D compound model that corrects DVA. If ``va_scale`` is `None` or 1 then `astropy.modeling.models.Identity` will be returned. """ if va_scale is None or va_scale == 1: return Identity(2) if va_scale <= 0: raise ValueError( "'Velocity aberration scale must be a positive number.") va_corr = Scale(va_scale, name='dva_scale_v2') & Scale(va_scale, name='dva_scale_v3') if v2_ref is None: v2_ref = 0 if v3_ref is None: v3_ref = 0 if v2_ref == 0 and v3_ref == 0: return va_corr # NOTE: it is assumed that v2, v3 angles and va scale are small enough # so that for expected scale factors the issue of angle wrapping # (180 degrees) can be neglected. v2_shift = (1 - va_scale) * v2_ref v3_shift = (1 - va_scale) * v3_ref va_corr |= Shift(v2_shift, name='dva_v2_shift') & Shift( v3_shift, name='dva_v3_shift') va_corr.name = 'DVA_Correction' return va_corr
def test_regions_selector(tmpdir): m1 = Mapping([0, 1, 1]) | Shift(1) & Shift(2) & Shift(3) m2 = Mapping([0, 1, 1]) | Scale(2) & Scale(3) & Scale(3) sel = {1: m1, 2: m2} a = np.zeros((5, 6), dtype=np.int32) a[:, 1:3] = 1 a[:, 4:5] = 2 mask = selector.LabelMapperArray(a) rs = selector.RegionsSelector(inputs=('x', 'y'), outputs=('ra', 'dec', 'lam'), selector=sel, label_mapper=mask) assert_selector_roundtrip(rs, tmpdir)
def test_name(): offx = Shift(1) scl = Scale(2) m = offx | scl scl.name = "scale" assert m.submodel_names == ('None_0', 'scale') assert m.name is None m.name = "M" assert m.name == "M" m1 = m.rename("M1") assert m.name == "M1" assert m1.name == "M1"
def test_name(): offx = Shift(1) scl = Scale(2) m = offx | scl scl.name = "scale" assert m._submodel_names == ('None_0', 'None_1') assert m.name is None m.name = "M" assert m.name == "M" m1 = m.rename("M1") assert m.name == "M" assert m1.name == "M1"
def test_mapping_inverse(): """Tests inverting a compound model that includes a `Mapping`.""" rs1 = Rotation2D(12.1) & Scale(13.2) rs2 = Rotation2D(14.3) & Scale(15.4) # Rotates 2 of the coordinates and scales the third--then rotates on a # different axis and scales on the axis of rotation. No physical meaning # here just a simple test m = rs1 | Mapping([2, 0, 1]) | rs2 assert_allclose((0, 1, 2), m.inverse(*m(0, 1, 2)), atol=1e-08)
def test_regions_selector(tmpdir): m1 = Mapping([0, 1, 1]) | Shift(1) & Shift(2) & Shift(3) m2 = Mapping([0, 1, 1]) | Scale(2) & Scale(3) & Scale(3) sel = {1:m1, 2:m2} a = np.zeros((5,6), dtype=np.int32) a[:, 1:3] = 1 a[:, 4:5] = 2 mask = selector.LabelMapperArray(a) rs = selector.RegionsSelector(inputs=('x', 'y'), outputs=('ra', 'dec', 'lam'), selector=sel, label_mapper=mask) tree = {'model': rs} helpers.assert_roundtrip_tree(tree, tmpdir, extensions=GWCSExtension())
def imaging_distortion(input_model, reference_files): distortion = AsdfFile.open(reference_files['distortion']).tree['model'] # Convert to deg - output of distortion models is in arcsec. transform = distortion | Scale(1 / 3600) & Scale(1 / 3600) try: bb = transform.bounding_box except NotImplementedError: shape = input_model.data.shape # Note: Since bounding_box is attached to the model here it's in reverse order. transform.bounding_box = ((-0.5, shape[0] - 0.5), (-0.5, shape[1] - 0.5)) return transform
def test_compound_custom_inverse(): """ Test that a compound model with a custom inverse has that inverse applied when the inverse of another model, of which it is a component, is computed. Regression test for https://github.com/astropy/astropy/issues/3542 """ poly = Polynomial1D(1, c0=1, c1=2) scale = Scale(1) shift = Shift(1) model1 = poly | scale model1.inverse = poly # model1 now has a custom inverse (the polynomial itself, ignoring the # trivial scale factor) model2 = shift | model1 assert_allclose(model2.inverse(1), (poly | shift.inverse)(1)) # Make sure an inverse is not allowed if the models were combined with the # wrong operator, or if one of the models doesn't have an inverse defined with pytest.raises(NotImplementedError): (shift + model1).inverse with pytest.raises(NotImplementedError): (model1 & poly).inverse
def test_compound_input_units_strict(): """ Test setting input_units_strict on one of the models. """ class ScaleDegrees(Scale): input_units = {'x': u.deg} s1 = ScaleDegrees(2) s2 = Scale(2) cs = s1 | s2 out = cs(10 * u.arcsec) assert_quantity_allclose(out, 40 * u.arcsec) assert out.unit is u.deg # important since this tests input_units_strict cs = s2 | s1 out = cs(10 * u.arcsec) assert_quantity_allclose(out, 40 * u.arcsec) assert out.unit is u.deg # important since this tests input_units_strict cs = s1 & s2 out = cs(10 * u.arcsec, 10 * u.arcsec) assert_quantity_allclose(out, 20 * u.arcsec) assert out[0].unit is u.deg assert out[1].unit is u.arcsec
def _make_reference_gwcs_wcs(fits_hdr): hdr = fits.Header.fromfile(get_pkg_data_filename(fits_hdr)) fw = fitswcs.WCS(hdr) unit_conv = Scale(1.0 / 3600.0, name='arcsec_to_deg_1D') unit_conv = unit_conv & unit_conv unit_conv.name = 'arcsec_to_deg_2D' unit_conv_inv = Scale(3600.0, name='deg_to_arcsec_1D') unit_conv_inv = unit_conv_inv & unit_conv_inv unit_conv_inv.name = 'deg_to_arcsec_2D' c2s = CartesianToSpherical(name='c2s', wrap_lon_at=180) s2c = SphericalToCartesian(name='s2c', wrap_lon_at=180) c2tan = ((Mapping((0, 1, 2), name='xyz') / Mapping( (0, 0, 0), n_inputs=3, name='xxx')) | Mapping((1, 2), name='xtyt')) c2tan.name = 'Cartesian 3D to TAN' tan2c = (Mapping((0, 0, 1), n_inputs=2, name='xtyt2xyz') | (Const1D(1, name='one') & Identity(2, name='I(2D)'))) tan2c.name = 'TAN to cartesian 3D' tan2c.inverse = c2tan c2tan.inverse = tan2c aff = AffineTransformation2D(matrix=fw.wcs.cd) offx = Shift(-fw.wcs.crpix[0]) offy = Shift(-fw.wcs.crpix[1]) s = 5e-6 scale = Scale(s) & Scale(s) det2tan = (offx & offy) | scale | tan2c | c2s | unit_conv_inv taninv = s2c | c2tan tan = Pix2Sky_TAN() n2c = RotateNative2Celestial(fw.wcs.crval[0], fw.wcs.crval[1], 180) wcslin = unit_conv | taninv | scale.inverse | aff | tan | n2c sky_frm = cf.CelestialFrame(reference_frame=coord.ICRS()) det_frm = cf.Frame2D(name='detector') v2v3_frm = cf.Frame2D(name="v2v3", unit=(u.arcsec, u.arcsec), axes_names=('x', 'y'), axes_order=(0, 1)) pipeline = [(det_frm, det2tan), (v2v3_frm, wcslin), (sky_frm, None)] gw = gwcs.WCS(input_frame=det_frm, output_frame=sky_frm, forward_transform=pipeline) gw.crpix = fw.wcs.crpix gw.crval = fw.wcs.crval gw.bounding_box = ((-0.5, fw.pixel_shape[0] - 0.5), (-0.5, fw.pixel_shape[1] - 0.5)) return gw
def test_v2v3todet_roundtrips(): s2c = (Scale(1.0 / 3600.0) & Scale(1.0 / 3600.0)) | SphericalToCartesian(wrap_lon_at=180) s = 1.0e-5 crpix = np.random.random(2) alpha = 0.25 * np.pi * np.random.random() x, y = 1024 * np.random.random(2) v2, v3 = 45 * np.random.random(2) cd = [[s * np.cos(alpha), -s * np.sin(alpha)], [s * np.sin(alpha), s * np.cos(alpha)]] d2v = create_DetToV2V3(v2ref=123.0, v3ref=500.0, roll=15.0, crpix=crpix, cd=cd) v2d = create_V2V3ToDet(v2ref=123.0, v3ref=500.0, roll=15.0, crpix=crpix, cd=cd) assert np.allclose(d2v.inverse(*d2v(x, y)), (x, y), rtol=1e3 * _ATOL, atol=1e3 * _ATOL) assert (np.allclose(s2c(*v2d.inverse(*v2d(v2, v3))), s2c(v2, v3), rtol=1e3 * _ATOL, atol=_ATOL) or np.allclose(-np.asanyarray(s2c(*v2d.inverse(*v2d(v2, v3)))), s2c(v2, v3), rtol=1e3 * _ATOL, atol=_ATOL)) assert np.allclose(v2d(*d2v(x, y)), (x, y), rtol=1e5 * _ATOL, atol=1e3 * _ATOL) assert (np.allclose(s2c(*d2v(*v2d(v2, v3))), s2c(v2, v3), rtol=1e3 * _ATOL, atol=1e3 * _ATOL) or np.allclose(-np.asanyarray(s2c(*d2v(*v2d(v2, v3)))), s2c(v2, v3), rtol=1e3 * _ATOL, atol=1e3 * _ATOL))
def v23tosky(input_model, wrap_v2_at=180, wrap_lon_at=360): v2_ref = input_model.meta.wcsinfo.v2_ref / 3600 v3_ref = input_model.meta.wcsinfo.v3_ref / 3600 roll_ref = input_model.meta.wcsinfo.roll_ref ra_ref = input_model.meta.wcsinfo.ra_ref dec_ref = input_model.meta.wcsinfo.dec_ref angles = np.array([v2_ref, -v3_ref, roll_ref, dec_ref, -ra_ref]) axes = "zyxyz" rot = RotationSequence3D(angles, axes_order=axes) # The sky rotation expects values in deg. # This should be removed when models work with quantities. m = ((Scale(1 / 3600) & Scale(1 / 3600)) | SphericalToCartesian(wrap_lon_at=wrap_v2_at) | rot | CartesianToSpherical(wrap_lon_at=wrap_lon_at)) m.name = 'v23tosky' return m
def test_simple_two_model_compose_1d(): """ Shift and Scale are two of the simplest models to test model composition with. """ S1 = Shift(2) | Scale(3) # First shift then scale assert isinstance(S1, CompoundModel) assert S1.n_inputs == 1 assert S1.n_outputs == 1 assert S1(1) == 9.0 S2 = Scale(2) | Shift(3) # First scale then shift assert isinstance(S2, CompoundModel) assert S2.n_inputs == 1 assert S2.n_outputs == 1 assert S2(1) == 5.0 # Test with array inputs assert_array_equal(S2([1, 2, 3]), [5.0, 7.0, 9.0])
def test_update_parameters(): offx = Shift(1) scl = Scale(2) m = offx | scl assert (m(1) == 4) offx.offset = 42 assert (m(1) == 4) m.factor_1 = 100 assert (m(1) == 200) m2 = m | offx assert (m2(1) == 242)
def test_update_parameters(): offx = Shift(1) scl = Scale(2) m = offx | scl assert m(1) == 4 offx.offset = 42 assert m(1) == 86 m.factor_1 = 100 assert m(1) == 4300 m2 = m | offx assert m2(1) == 4342
def test_slicing_on_instances_2(): """ More slicing tests. Regression test for https://github.com/embray/astropy/pull/10 """ model_a = Shift(1, name='a') model_b = Shift(2, name='b') model_c = Rotation2D(3, name='c') model_d = Scale(2, name='d') model_e = Scale(3, name='e') m = (model_a & model_b) | model_c | (model_d & model_e) with pytest.raises(ModelDefinitionError): # The slice can't actually be taken since the resulting model cannot be # evaluated assert m[1:].submodel_names == ('b', 'c', 'd', 'e') assert m[:].submodel_names == ('a', 'b', 'c', 'd', 'e') assert m['a':].submodel_names == ('a', 'b', 'c', 'd', 'e') with pytest.raises(ModelDefinitionError): assert m['c':'d'].submodel_names == ('c', 'd') assert m[1:2].name == 'b' assert m[2:7].submodel_names == ('c', 'd', 'e') with pytest.raises(IndexError): m['x'] with pytest.raises(IndexError): m['a':'r'] with pytest.raises(ModelDefinitionError): assert m[-4:4].submodel_names == ('b', 'c', 'd') with pytest.raises(ModelDefinitionError): assert m[-4:-2].submodel_names == ('b', 'c')
def __mul__(self, other): """Multiply self and other.""" self._validate_other_mul_div(other) if isinstance(other, (u.Quantity, numbers.Number)): newcls = self.__class__(modelclass=self.model | Scale(other)) elif isinstance(other, BaseUnitlessSpectrum): newcls = self.__class__(modelclass=self.model * other.model) else: # Source spectrum raise exceptions.IncompatibleSources( 'Cannot multiply two source spectra together') self._merge_meta(self, other, newcls) return newcls
def test_replace_submodel(): """ Replace a model in a Compound model """ S1 = Shift(2, name='shift2') | Scale( 3, name='scale3') # First shift then scale S2 = Scale(2, name='scale2') | Shift( 3, name='shift3') # First scale then shift m = S1 & S2 assert m(1, 2) == (9, 7) m2 = m.replace_submodel('scale3', Scale(4, name='scale4')) assert m2(1, 2) == (12, 7) assert m(1, 2) == (9, 7) # Check the inverse has been updated assert m2.inverse(12, 7) == (1, 2) # Produce the same result by replacing a single model with a compound m3 = m.replace_submodel('shift2', Shift(2) | Scale(2)) assert m(1, 2) == (9, 7) assert m3(1, 2) == (18, 7) # Check the inverse has been updated assert m3.inverse(18, 7) == (1, 2) # Test with arithmetic model compunding operator m = S1 + S2 assert m(1) == 14 m2 = m.replace_submodel('scale2', Scale(4, name='scale4')) assert m2(1) == 16 # Test with fix_inputs() R = fix_inputs(Rotation2D(angle=90, name='rotate'), {0: 1}) m4 = S1 | R assert_allclose(m4(0), (-6, 1)) m5 = m4.replace_submodel('rotate', Rotation2D(180)) assert_allclose(m5(0), (-1, -6)) # Check we get a value error when model name doesn't exist with pytest.raises(ValueError): m2 = m.replace_submodel('not_there', Scale(2)) # And now a model set P = Polynomial1D(degree=1, n_models=2, name='poly') S = Shift([1, 2], n_models=2) m = P | S assert_array_equal(m([0, 1]), (1, 2)) with pytest.raises(ValueError): m2 = m.replace_submodel('poly', Polynomial1D(degree=1, c0=1)) m2 = m.replace_submodel('poly', Polynomial1D(degree=1, c0=[1, 2], n_models=2)) assert_array_equal(m2([0, 1]), (2, 4))
def test_mapping_basic_permutations(): """ Tests a couple basic examples of the Mapping model--specifically examples that merely permute the outputs. """ x, y = Rotation2D(90)(1, 2) rs = Rotation2D(90) | Mapping((1, 0)) x_prime, y_prime = rs(1, 2) assert_allclose((x, y), (y_prime, x_prime)) # A more complicated permutation m = Rotation2D(90) & Scale(2) x, y, z = m(1, 2, 3) ms = m | Mapping((2, 0, 1)) x_prime, y_prime, z_prime = ms(1, 2, 3) assert_allclose((x, y, z), (y_prime, z_prime, x_prime))
def _tpcorr_init(v2_ref, v3_ref, roll_ref): s2c = SphericalToCartesian(name='s2c', wrap_lon_at=180) c2s = CartesianToSpherical(name='c2s', wrap_lon_at=180) unit_conv = Scale(1.0 / 3600.0, name='arcsec_to_deg_1D') unit_conv = unit_conv & unit_conv unit_conv.name = 'arcsec_to_deg_2D' unit_conv_inv = Scale(3600.0, name='deg_to_arcsec_1D') unit_conv_inv = unit_conv_inv & unit_conv_inv unit_conv_inv.name = 'deg_to_arcsec_2D' affine = AffineTransformation2D(name='tp_affine') affine_inv = AffineTransformation2D(name='tp_affine_inv') rot = RotationSequence3D([v2_ref, -v3_ref, roll_ref], 'zyx', name='det_to_optic_axis') rot_inv = rot.inverse rot_inv.name = 'optic_axis_to_det' # projection submodels: c2tan = ((Mapping((0, 1, 2), name='xyz') / Mapping( (0, 0, 0), n_inputs=3, name='xxx')) | Mapping((1, 2), name='xtyt')) c2tan.name = 'Cartesian 3D to TAN' tan2c = (Mapping((0, 0, 1), n_inputs=2, name='xtyt2xyz') | (Const1D(1, name='one') & Identity(2, name='I(2D)'))) tan2c.name = 'TAN to cartesian 3D' total_corr = (unit_conv | s2c | rot | c2tan | affine | tan2c | rot_inv | c2s | unit_conv_inv) total_corr.name = 'JWST tangent-plane linear correction. v1' inv_total_corr = (unit_conv | s2c | rot | c2tan | affine_inv | tan2c | rot_inv | c2s | unit_conv_inv) inv_total_corr.name = 'Inverse JWST tangent-plane linear correction. v1' # TODO # re-enable circular inverse definitions once # https://github.com/spacetelescope/asdf/issues/744 or # https://github.com/spacetelescope/asdf/issues/745 are resolved. # # inv_total_corr.inverse = total_corr total_corr.inverse = inv_total_corr return total_corr
def oteip_to_v23(reference_files): """ Transform from the OTEIP frame to the V2V3 frame. Parameters ---------- reference_files: dict Dictionary with reference files returned by CRDS. Returns ------- model : `~astropy.modeling.core.Model` model. Transform from OTEIP to V2V3. """ with AsdfFile.open(reference_files['ote']) as f: ote = f.tree['model'].copy() fore2ote_mapping = Identity(3, name='fore2ote_mapping') fore2ote_mapping.inverse = Mapping((0, 1, 2, 2)) # Create the transform to v2/v3/lambda. The wavelength units up to this point are # meters as required by the pipeline but the desired output wavelength units is microns. # So we are going to Scale the spectral units by 1e6 (meters -> microns) return fore2ote_mapping | (ote & Scale(1e6))
def test_compound_input_units_allow_dimensionless(): """ Test setting input_units_allow_dimensionless on one of the models. """ class ScaleDegrees(Scale): input_units = {'x': u.deg} s1 = ScaleDegrees(2) s1._input_units_allow_dimensionless = True s2 = Scale(2) cs = s1 | s2 cs = cs.rename('TestModel') out = cs(10) assert_quantity_allclose(out, 40 * u.one) out = cs(10 * u.arcsec) assert_quantity_allclose(out, 40 * u.arcsec) with pytest.raises(UnitsError) as exc: out = cs(10 * u.m) assert exc.value.args[ 0] == "TestModel: Units of input 'x', m (length), could not be converted to required input units of deg (angle)" s1._input_units_allow_dimensionless = False cs = s1 | s2 cs = cs.rename('TestModel') with pytest.raises(UnitsError) as exc: out = cs(10) assert exc.value.args[ 0] == "TestModel: Units of input 'x', (dimensionless), could not be converted to required input units of deg (angle)" s1._input_units_allow_dimensionless = True cs = s2 | s1 cs = cs.rename('TestModel') out = cs(10) assert_quantity_allclose(out, 40 * u.one) out = cs(10 * u.arcsec) assert_quantity_allclose(out, 40 * u.arcsec) with pytest.raises(UnitsError) as exc: out = cs(10 * u.m) assert exc.value.args[ 0] == "ScaleDegrees: Units of input 'x', m (length), could not be converted to required input units of deg (angle)" s1._input_units_allow_dimensionless = False cs = s2 | s1 with pytest.raises(UnitsError) as exc: out = cs(10) assert exc.value.args[ 0] == "ScaleDegrees: Units of input 'x', (dimensionless), could not be converted to required input units of deg (angle)" s1._input_units_allow_dimensionless = True s1 = ScaleDegrees(2) s1._input_units_allow_dimensionless = True s2 = ScaleDegrees(2) s2._input_units_allow_dimensionless = False cs = s1 & s2 cs = cs.rename('TestModel') out = cs(10, 10 * u.arcsec) assert_quantity_allclose(out[0], 20 * u.one) assert_quantity_allclose(out[1], 20 * u.arcsec) with pytest.raises(UnitsError) as exc: out = cs(10, 10) assert exc.value.args[ 0] == "TestModel: Units of input 'x1', (dimensionless), could not be converted to required input units of deg (angle)"
def _make_gwcs_wcs(fits_hdr): hdr = fits.Header.fromfile(get_pkg_data_filename(fits_hdr)) fw = fitswcs.WCS(hdr) a_order = hdr['A_ORDER'] a_coeff = {} for i in range(a_order + 1): for j in range(a_order + 1 - i): key = 'A_{:d}_{:d}'.format(i, j) if key in hdr: a_coeff[key] = hdr[key] b_order = hdr['B_ORDER'] b_coeff = {} for i in range(b_order + 1): for j in range(b_order + 1 - i): key = 'B_{:d}_{:d}'.format(i, j) if key in hdr: b_coeff[key] = hdr[key] cx = {"c" + k[2:]: v for k, v in a_coeff.items()} cy = {"c" + k[2:]: v for k, v in b_coeff.items()} sip_distortion = ((Shift(-fw.wcs.crpix[0]) & Shift(-fw.wcs.crpix[1])) | Mapping((0, 1, 0, 1)) | (polynomial.Polynomial2D(a_order, **cx, c1_0=1) & polynomial.Polynomial2D(b_order, **cy, c0_1=1)) | (Shift(fw.wcs.crpix[0]) & Shift(fw.wcs.crpix[1]))) y, x = np.indices(fw.array_shape) unit_conv = Scale(1.0 / 3600.0, name='arcsec_to_deg_1D') unit_conv = unit_conv & unit_conv unit_conv.name = 'arcsec_to_deg_2D' unit_conv_inv = Scale(3600.0, name='deg_to_arcsec_1D') unit_conv_inv = unit_conv_inv & unit_conv_inv unit_conv_inv.name = 'deg_to_arcsec_2D' c2s = CartesianToSpherical(name='c2s', wrap_lon_at=180) s2c = SphericalToCartesian(name='s2c', wrap_lon_at=180) c2tan = ((Mapping((0, 1, 2), name='xyz') / Mapping( (0, 0, 0), n_inputs=3, name='xxx')) | Mapping((1, 2), name='xtyt')) c2tan.name = 'Cartesian 3D to TAN' tan2c = (Mapping((0, 0, 1), n_inputs=2, name='xtyt2xyz') | (Const1D(1, name='one') & Identity(2, name='I(2D)'))) tan2c.name = 'TAN to cartesian 3D' tan2c.inverse = c2tan c2tan.inverse = tan2c aff = AffineTransformation2D(matrix=fw.wcs.cd) offx = Shift(-fw.wcs.crpix[0]) offy = Shift(-fw.wcs.crpix[1]) s = 5e-6 scale = Scale(s) & Scale(s) sip_distortion |= (offx & offy) | scale | tan2c | c2s | unit_conv_inv taninv = s2c | c2tan tan = Pix2Sky_TAN() n2c = RotateNative2Celestial(fw.wcs.crval[0], fw.wcs.crval[1], 180) wcslin = unit_conv | taninv | scale.inverse | aff | tan | n2c sky_frm = cf.CelestialFrame(reference_frame=coord.ICRS()) det_frm = cf.Frame2D(name='detector') v2v3_frm = cf.Frame2D(name="v2v3", unit=(u.arcsec, u.arcsec), axes_names=('x', 'y'), axes_order=(0, 1)) pipeline = [(det_frm, sip_distortion), (v2v3_frm, wcslin), (sky_frm, None)] gw = gwcs.WCS(input_frame=det_frm, output_frame=sky_frm, forward_transform=pipeline) gw.crpix = fw.wcs.crpix gw.crval = fw.wcs.crval gw.bounding_box = ((-0.5, fw.pixel_shape[0] - 0.5), (-0.5, fw.pixel_shape[1] - 0.5)) # sanity check: for _ in range(100): x = np.random.randint(1, fw.pixel_shape[0]) y = np.random.randint(1, fw.pixel_shape[1]) assert np.allclose(gw(x, y), fw.all_pix2world(x, y, 1), rtol=0, atol=1e-11) return gw
def build_nirspec_output_wcs(self, refwcs=None): """ Create a simple output wcs covering footprint of the input datamodels """ # TODO: generalize this for more than one input datamodel # TODO: generalize this for imaging modes with distorted wcs input_model = self.input_models[0] if refwcs == None: refwcs = input_model.meta.wcs # Generate grid of sky coordinates for area within bounding box bb = refwcs.bounding_box det = x, y = wcstools.grid_from_bounding_box(bb, step=(1, 1), center=True) sky = ra, dec, lam = refwcs(*det) x_center = int((bb[0][1] - bb[0][0]) / 2) y_center = int((bb[1][1] - bb[1][0]) / 2) log.debug("Center of bounding box: {} {}".format(x_center, y_center)) # Compute slit angular size, slit center sky coords xpos = [] sz = 3 for row in lam: if np.isnan(row[x_center]): xpos.append(np.nan) else: f = interpolate.interp1d(row[x_center - sz + 1:x_center + sz], x[y_center, x_center - sz + 1:x_center + sz], bounds_error=False, fill_value='extrapolate') xpos.append(f(lam[y_center, x_center])) x_arg = np.array(xpos)[~np.isnan(lam[:, x_center])] y_arg = y[~np.isnan(lam[:, x_center]), x_center] # slit_coords, spect0 = refwcs(x_arg, y_arg, output='numericals_plus') slit_ra, slit_dec, slit_spec_ref = refwcs(x_arg, y_arg) slit_coords = SkyCoord(ra=slit_ra, dec=slit_dec, unit=u.deg) pix_num = np.flipud(np.arange(len(slit_ra))) # pix_num = np.arange(len(slit_ra)) interpol_ra = interpolate.interp1d(pix_num, slit_ra) interpol_dec = interpolate.interp1d(pix_num, slit_dec) slit_center_pix = len(slit_spec_ref) / 2. - 1 log.debug('Slit center pix: {0}'.format(slit_center_pix)) slit_center_sky = SkyCoord(ra=interpol_ra(slit_center_pix), dec=interpol_dec(slit_center_pix), unit=u.deg) log.debug('Slit center: {0}'.format(slit_center_sky)) log.debug('Fiducial: {0}'.format( resample_utils.compute_spec_fiducial([refwcs]))) angular_slit_size = np.abs(slit_coords[0].separation(slit_coords[-1])) log.debug('Slit angular size: {0}'.format(angular_slit_size.arcsec)) dra, ddec = slit_coords[0].spherical_offsets_to(slit_coords[-1]) offset_up_slit = (dra.to(u.arcsec), ddec.to(u.arcsec)) log.debug('Offset up the slit: {0}'.format(offset_up_slit)) # Compute spatial and spectral scales xposn = np.array(xpos)[~np.isnan(xpos)] dx = xposn[-1] - xposn[0] slit_npix = np.sqrt(dx**2 + np.array(len(xposn) - 1)**2) spatial_scale = angular_slit_size / slit_npix log.debug('Spatial scale: {0}'.format(spatial_scale.arcsec)) spectral_scale = lam[y_center, x_center] - lam[y_center, x_center - 1] # Compute slit angle relative (clockwise) to y axis slit_rot_angle = (np.arcsin(dx / slit_npix) * u.radian).to(u.degree) slit_rot_angle = slit_rot_angle.value log.debug('Slit rotation angle: {0}'.format(slit_rot_angle)) # Compute transform for output frame roll_ref = input_model.meta.wcsinfo.roll_ref min_lam = np.nanmin(lam) offset = Shift(-slit_center_pix) & Shift(-slit_center_pix) # TODO: double-check the signs on the following rotation angles rot = Rotation2D(roll_ref + slit_rot_angle) scale = Scale(spatial_scale.value) & Scale(spatial_scale.value) tan = Pix2Sky_TAN() lon_pole = _compute_lon_pole(slit_center_sky, tan) skyrot = RotateNative2Celestial(slit_center_sky.ra.value, slit_center_sky.dec.value, lon_pole.value) spatial_trans = offset | rot | scale | tan | skyrot spectral_trans = Scale(spectral_scale) | Shift(min_lam) mapping = Mapping((1, 1, 0)) mapping.inverse = Mapping((2, 1)) transform = mapping | spatial_trans & spectral_trans transform.outputs = ('ra', 'dec', 'lamda') # Build the output wcs input_frame = refwcs.input_frame output_frame = refwcs.output_frame wnew = WCS(output_frame=output_frame, forward_transform=transform) # Build the bounding_box in the output frame wcs object bounding_box_grid = wnew.backward_transform(ra, dec, lam) bounding_box = [] for axis in input_frame.axes_order: axis_min = np.nanmin(bounding_box_grid[axis]) axis_max = np.nanmax(bounding_box_grid[axis]) bounding_box.append((axis_min, axis_max)) wnew.bounding_box = tuple(bounding_box) # Update class properties self.output_spatial_scale = spatial_scale self.output_spectral_scale = spectral_scale self.output_wcs = wnew
def build_miri_output_wcs(self, refwcs=None): """ Create a simple output wcs covering footprint of the input datamodels """ # TODO: generalize this for more than one input datamodel # TODO: generalize this for imaging modes with distorted wcs input_model = self.input_models[0] if refwcs == None: refwcs = input_model.meta.wcs x, y = wcstools.grid_from_bounding_box(refwcs.bounding_box, step=(1, 1), center=True) ra, dec, lam = refwcs(x.flatten(), y.flatten()) # TODO: once astropy.modeling._Tabular is fixed, take out the # flatten() and reshape() code above and below ra = ra.reshape(x.shape) dec = dec.reshape(x.shape) lam = lam.reshape(x.shape) # Find rotation of the slit from y axis from the wcs forward transform # TODO: figure out if angle is necessary for MIRI. See for discussion # https://github.com/STScI-JWST/jwst/pull/347 rotation = [m for m in refwcs.forward_transform if \ isinstance(m, Rotation2D)] if rotation: rot_slit = functools.reduce(lambda x, y: x | y, rotation) rot_angle = rot_slit.inverse.angle.value unrotate = rot_slit.inverse refwcs_minus_rot = refwcs.forward_transform | \ unrotate & Identity(1) # Correct for this rotation in the wcs ra, dec, lam = refwcs_minus_rot(x.flatten(), y.flatten()) ra = ra.reshape(x.shape) dec = dec.reshape(x.shape) lam = lam.reshape(x.shape) # Get the slit size at the center of the dispersion sky_coords = SkyCoord(ra=ra, dec=dec, unit=u.deg) slit_coords = sky_coords[int(sky_coords.shape[0] / 2)] slit_angular_size = slit_coords[0].separation(slit_coords[-1]) log.debug('Slit angular size: {0}'.format(slit_angular_size.arcsec)) # Compute slit center from bounding_box dx0 = refwcs.bounding_box[0][0] dx1 = refwcs.bounding_box[0][1] dy0 = refwcs.bounding_box[1][0] dy1 = refwcs.bounding_box[1][1] slit_center_pix = (dx1 - dx0) / 2 dispersion_center_pix = (dy1 - dy0) / 2 slit_center = refwcs_minus_rot(dx0 + slit_center_pix, dy0 + dispersion_center_pix) slit_center_sky = SkyCoord(ra=slit_center[0], dec=slit_center[1], unit=u.deg) log.debug('slit center: {0}'.format(slit_center)) # Compute spatial and spectral scales spatial_scale = slit_angular_size / slit_coords.shape[0] log.debug('Spatial scale: {0}'.format(spatial_scale.arcsec)) tcenter = int((dx1 - dx0) / 2) trace = lam[:, tcenter] trace = trace[~np.isnan(trace)] spectral_scale = np.abs((trace[-1] - trace[0]) / trace.shape[0]) log.debug('spectral scale: {0}'.format(spectral_scale)) # Compute transform for output frame log.debug('Slit center %s' % slit_center_pix) offset = Shift(-slit_center_pix) & Shift(-slit_center_pix) # TODO: double-check the signs on the following rotation angles roll_ref = input_model.meta.wcsinfo.roll_ref * u.deg rot = Rotation2D(roll_ref) tan = Pix2Sky_TAN() lon_pole = _compute_lon_pole(slit_center_sky, tan) skyrot = RotateNative2Celestial(slit_center_sky.ra, slit_center_sky.dec, lon_pole) min_lam = np.nanmin(lam) mapping = Mapping((0, 0, 1)) transform = Shift(-slit_center_pix) & Identity(1) | \ Scale(spatial_scale) & Scale(spectral_scale) | \ Identity(1) & Shift(min_lam) | mapping | \ (rot | tan | skyrot) & Identity(1) transform.inputs = (x, y) transform.outputs = ('ra', 'dec', 'lamda') # Build the output wcs input_frame = refwcs.input_frame output_frame = refwcs.output_frame wnew = WCS(output_frame=output_frame, forward_transform=transform) # Build the bounding_box in the output frame wcs object bounding_box_grid = wnew.backward_transform(ra, dec, lam) bounding_box = [] for axis in input_frame.axes_order: axis_min = np.nanmin(bounding_box_grid[axis]) axis_max = np.nanmax(bounding_box_grid[axis]) bounding_box.append((axis_min, axis_max)) wnew.bounding_box = tuple(bounding_box) # Update class properties self.output_spatial_scale = spatial_scale self.output_spectral_scale = spectral_scale self.output_wcs = wnew
def test_compound_input_units_allow_dimensionless(): """ Test setting input_units_allow_dimensionless on one of the models. """ class ScaleDegrees(Scale): input_units = {'x': u.deg} s1 = ScaleDegrees(2) s1._input_units_allow_dimensionless = True s2 = Scale(2) cs = s1 | s2 cs = cs.rename('TestModel') out = cs(10) assert_quantity_allclose(out, 40 * u.one) out = cs(10 * u.arcsec) assert_quantity_allclose(out, 40 * u.arcsec) with pytest.raises(UnitsError) as exc: out = cs(10 * u.m) assert exc.value.args[0] == "TestModel: Units of input 'x', m (length), could not be converted to required input units of deg (angle)" s1._input_units_allow_dimensionless = False cs = s1 | s2 cs = cs.rename('TestModel') with pytest.raises(UnitsError) as exc: out = cs(10) assert exc.value.args[0] == "TestModel: Units of input 'x', (dimensionless), could not be converted to required input units of deg (angle)" s1._input_units_allow_dimensionless = True cs = s2 | s1 cs = cs.rename('TestModel') out = cs(10) assert_quantity_allclose(out, 40 * u.one) out = cs(10 * u.arcsec) assert_quantity_allclose(out, 40 * u.arcsec) with pytest.raises(UnitsError) as exc: out = cs(10 * u.m) assert exc.value.args[0] == "ScaleDegrees: Units of input 'x', m (length), could not be converted to required input units of deg (angle)" s1._input_units_allow_dimensionless = False cs = s2 | s1 with pytest.raises(UnitsError) as exc: out = cs(10) assert exc.value.args[0] == "ScaleDegrees: Units of input 'x', (dimensionless), could not be converted to required input units of deg (angle)" s1._input_units_allow_dimensionless = True s1 = ScaleDegrees(2) s1._input_units_allow_dimensionless = True s2 = ScaleDegrees(2) s2._input_units_allow_dimensionless = False cs = s1 & s2 cs = cs.rename('TestModel') out = cs(10, 10 * u.arcsec) assert_quantity_allclose(out[0], 20 * u.one) assert_quantity_allclose(out[1], 20 * u.arcsec) with pytest.raises(UnitsError) as exc: out = cs(10, 10) assert exc.value.args[0] == "TestModel: Units of input 'x1', (dimensionless), could not be converted to required input units of deg (angle)"