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_compound_model_with_nonstandard_broadcasting(): """ Ensure that the ``standard_broadcasting`` flag is properly propagated when creating compound models. See the commit message for the commit in which this was added for more details. """ offx = Shift(1) offy = Shift(2) rot = AffineTransformation2D([[0, -1], [1, 0]]) m = (offx & offy) | rot x, y = m(0, 0) assert x == -2 assert y == 1 # make sure conversion back to scalars is working properly assert isinstance(x, float) assert isinstance(y, float) x, y = m([0, 1, 2], [0, 1, 2]) assert np.all(x == [-2, -3, -4]) assert np.all(y == [1, 2, 3])
def test_slicing_on_instance_with_parameterless_model(): """ Regression test to fix an issue where the indices attached to parameter names on a compound model were not handled properly when one or more submodels have no parameters. This was especially evident in slicing. """ p2 = Polynomial2D(1, c0_0=1, c1_0=2, c0_1=3) p1 = Polynomial2D(1, c0_0=1, c1_0=2, c0_1=3) mapping = Mapping((0, 1, 0, 1)) offx = Shift(-2, name='x_translation') offy = Shift(-1, name='y_translation') aff = AffineTransformation2D(matrix=[[1, 2], [3, 4]], name='rotation') model = mapping | (p1 & p2) | (offx & offy) | aff assert model.param_names == ('c0_0_1', 'c1_0_1', 'c0_1_1', 'c0_0_2', 'c1_0_2', 'c0_1_2', 'offset_3', 'offset_4', 'matrix_5', 'translation_5') assert model(1, 2) == (23.0, 53.0) m = model[3:] assert m.param_names == ('offset_3', 'offset_4', 'matrix_5', 'translation_5') assert m(1, 2) == (1.0, 1.0)
def get_refpix(siaf_instance, apername): """Return the reference location within the given aperture Parameters ---------- siaf_instance : pysiaf.Siaf('nircam') """ siaf_aperture = siaf_instance[apername] xref = siaf_aperture.XSciRef yref = siaf_aperture.YSciRef #return Shift(-xref) & Shift(-yref) # Check to see if we can use coeffs from a subarray aperture # and have them apply to all apertures. Need to update the shift # in that case by adding the distance from detector (0, 0) to the # lower left corner of the aperture #siaf = pysiaf.Siaf('nircam') xc, yc = sci_subarray_corners('nircam', apername, siaf=siaf_instance, verbose=False) llx, urx = xc lly, ury = yc print('Lower left corner x and y:', llx, lly) return Shift(-xref - llx) & Shift(-yref - lly)
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 test_compound_input_units_equivalencies(): """ Test setting input_units_equivalencies on one of the models. """ s1 = Shift(10 * u.deg) s1.input_units_equivalencies = {'x': u.pixel_scale(0.5 * u.deg / u.pix)} s2 = Shift(10 * u.deg) sp = Shift(10 * u.pix) cs = s1 | s2 out = cs(10 * u.pix) assert_quantity_allclose(out, 25 * u.deg) cs = sp | s1 out = cs(10 * u.pix) assert_quantity_allclose(out, 20 * u.deg) cs = s1 & s2 cs = cs.rename('TestModel') out = cs(20 * u.pix, 10 * u.deg) assert_quantity_allclose(out, 20 * u.deg) with pytest.raises(UnitsError) as exc: out = cs(20 * u.pix, 10 * u.pix) assert exc.value.args[ 0] == "TestModel: Units of input 'x1', pix (unknown), could not be converted to required input units of deg (angle)"
def test_compound_input_units_equivalencies(): """ Test setting input_units_equivalencies on one of the models. """ s1 = Shift(10 * u.deg) s1.input_units_equivalencies = {'x': u.pixel_scale(0.5 * u.deg / u.pix)} s2 = Shift(10 * u.deg) sp = Shift(10 * u.pix) cs = s1 | s2 out = cs(10 * u.pix) assert_quantity_allclose(out, 25 * u.deg) cs = sp | s1 out = cs(10 * u.pix) assert_quantity_allclose(out, 20 * u.deg) cs = s1 & s2 cs = cs.rename('TestModel') out = cs(20 * u.pix, 10 * u.deg) assert_quantity_allclose(out, 20 * u.deg) with pytest.raises(UnitsError) as exc: out = cs(20 * u.pix, 10 * u.pix) assert exc.value.args[0] == "TestModel: Units of input 'x1', pix (unknown), could not be converted to required input units of deg (angle)"
def spatial_like_model(): crpix1, crpix2 = (100, 100) * u.pix cdelt1, cdelt2 = (10, 10) * (u.arcsec / u.pix) shiftu = Shift(-crpix1) & Shift(-crpix2) scale = Multiply(cdelt1) & Multiply(cdelt2) return (shiftu | scale | Pix2Sky_AZP()) & Identity(1)
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 wcs_from_spec_footprints(wcslist, refwcs=None, transform=None, domain=None): """ Create a WCS from a list of spatial/spectral WCS. Build-7 workaround. """ if not isiterable(wcslist): raise ValueError("Expected 'wcslist' to be an iterable of gwcs.WCS") if not all([isinstance(w, WCS) for w in wcslist]): raise TypeError( "All items in 'wcslist' must have instance of gwcs.WCS") if refwcs is None: refwcs = wcslist[0] else: if not isinstance(refwcs, WCS): raise TypeError("Expected refwcs to be an instance of gwcs.WCS.") # TODO: generalize an approach to do this for more than one wcs. For # now, we just do it for one, using the api for a list of wcs. # Compute a fiducial point for the output frame at center of input data fiducial = compute_spec_fiducial(wcslist, domain=domain) # Create transform for output frame transform = compute_spec_transform(fiducial, refwcs) output_frame = refwcs.output_frame wnew = WCS(output_frame=output_frame, forward_transform=transform) # Build the domain in the output frame wcs object by running the input wcs # footprints through the backward transform of the output wcs sky = [spec_footprint(w) for w in wcslist] domain_grid = [wnew.backward_transform(*f) for f in sky] sky0 = sky[0] det = domain_grid[0] offsets = [] input_frame = refwcs.input_frame for axis in input_frame.axes_order: axis_min = np.nanmin(det[axis]) offsets.append(axis_min) transform = Shift(offsets[0]) & Shift(offsets[1]) | transform wnew = WCS(output_frame=output_frame, input_frame=input_frame, forward_transform=transform) domain = [] for axis in input_frame.axes_order: axis_min = np.nanmin(domain_grid[0][axis]) axis_max = np.nanmax(domain_grid[0][axis]) + 1 domain.append({ 'lower': axis_min, 'upper': axis_max, 'includes_lower': True, 'includes_upper': False }) wnew.domain = domain return wnew
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 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_compound_incompatible_units_fail(): """ Test incompatible model units in chain. """ s1 = Shift(10 * u.pix) s2 = Shift(10 * u.deg) cs = s1 | s2 with pytest.raises(UnitsError): cs(10 * u.pix)
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 imaging_distortion(input_model, reference_files): """ Create the "detector" to "v2v3" transform for imaging mode. Parameters ---------- input_model : `~jwst.datamodel.DataModel` Input datamodel for processing reference_files : dict The dictionary of reference file names and their associated files. Returns ------- The transform model """ dist = DistortionModel(reference_files['distortion']) transform = dist.model try: bbox = transform.bounding_box except NotImplementedError: # Check if the transform in the reference file has a ``bounding_box``. # If not set a ``bounding_box`` equal to the size of the image after # assembling all distortion corrections. bbox = None dist.close() # Add an offset for the filter if reference_files['filteroffset'] is not None: obsfilter = input_model.meta.instrument.filter obspupil = input_model.meta.instrument.pupil with asdf.open(reference_files['filteroffset']) as filter_offset: filters = filter_offset.tree['filters'] match_keys = {'filter': obsfilter, 'pupil': obspupil} row = find_row(filters, match_keys) if row is not None: col_offset = row.get('column_offset', 'N/A') row_offset = row.get('row_offset', 'N/A') log.debug( f"Offsets from filteroffset file are {col_offset}, {row_offset}" ) if col_offset != 'N/A' and row_offset != 'N/A': transform = Shift(col_offset) & Shift(row_offset) | transform else: log.debug("No match in fitleroffset file.") if bbox is None: transform.bounding_box = transform_bbox_from_shape( input_model.data.shape) else: transform.bounding_box = bbox return transform
def test_compound_pipe_equiv_call(): """ Check that equivalencies work when passed to evaluate, for a chained model (which has one input). """ s1 = Shift(10 * u.deg) s2 = Shift(10 * u.deg) cs = s1 | s2 out = cs(10 * u.pix, equivalencies={'x': u.pixel_scale(0.5 * u.deg / u.pix)}) assert_quantity_allclose(out, 25 * u.deg)
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 test_compound_input_units(): """ Test units to first model in chain. """ s1 = Shift(10 * u.deg) s2 = Shift(10 * u.deg) cs = s1 | s2 out = cs(10 * u.arcsecond) assert_quantity_allclose(out, 20 * u.deg + 10 * u.arcsec)
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_and_input_units(): """ Test units to first model in chain. """ s1 = Shift(10 * u.deg) s2 = Shift(10 * u.deg) cs = s1 & s2 out = cs(10 * u.arcsecond, 20 * u.arcsecond) assert_quantity_allclose(out[0], 10 * u.deg + 10 * u.arcsec) assert_quantity_allclose(out[1], 10 * u.deg + 20 * u.arcsec)
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) == 4) m.factor_1 = 100 assert(m(1) == 200) m2 = m | offx assert(m2(1) == 242)
def assign_moving_target_wcs(input_model): if not isinstance(input_model, datamodels.ModelContainer): raise ValueError("Expected a ModelContainer object") # Get the MT RA/Dec values from all the input exposures mt_ra = np.array( [model.meta.wcsinfo.mt_ra for model in input_model._models]) mt_dec = np.array( [model.meta.wcsinfo.mt_dec for model in input_model._models]) # Compute the mean MT RA/Dec over all exposures if (None in mt_ra) or (None in mt_dec): log.warning("One or more MT RA/Dec values missing in input images") log.warning("Step will be skipped, resulting in target misalignment") for model in input_model: model.meta.cal_step.assign_mtwcs = 'SKIPPED' return input_model else: mt_avra = mt_ra.mean() mt_avdec = mt_dec.mean() for model in input_model: pipeline = model.meta.wcs._pipeline[:-1] mt = deepcopy(model.meta.wcs.output_frame) mt.name = 'moving_target' mt_ra = model.meta.wcsinfo.mt_ra mt_dec = model.meta.wcsinfo.mt_dec model.meta.wcsinfo.mt_avra = mt_avra model.meta.wcsinfo.mt_avdec = mt_avdec rdel = mt_avra - mt_ra ddel = mt_avdec - mt_dec if isinstance(mt, cf.CelestialFrame): transform_to_mt = Shift(rdel) & Shift(ddel) elif isinstance(mt, cf.CompositeFrame): transform_to_mt = Shift(rdel) & Shift(ddel) & Identity(1) else: raise ValueError("Unrecognized coordinate frame.") pipeline.append((model.meta.wcs.output_frame, transform_to_mt)) pipeline.append((mt, None)) new_wcs = WCS(pipeline) del model.meta.wcs model.meta.wcs = new_wcs model.meta.cal_step.assign_mtwcs = 'COMPLETE' return input_model
def test_is_wcs_correction_small(offset, is_good): path = os.path.join(os.path.dirname(__file__), "mosaic_long_i2d_gwcs.asdf") with asdf.open(path) as af: wcs = af.tree["wcs"] # Make a copy and add an offset at the end of the transform twcs = deepcopy(wcs) step = twcs.pipeline[0] step.transform = step.transform | Shift(offset) & Shift(offset) twcs.bounding_box = wcs.bounding_box step = TweakRegStep() assert step._is_wcs_correction_small(wcs, twcs) == is_good
def test_compound_and_equiv_call(): """ Check that equivalencies work when passed to evaluate, for a composite model with two inputs. """ s1 = Shift(10 * u.deg) s2 = Shift(10 * u.deg) cs = s1 & s2 out = cs(10 * u.pix, 10 * u.pix, equivalencies={'x0': u.pixel_scale(0.5 * u.deg / u.pix), 'x1': u.pixel_scale(0.5 * u.deg / u.pix)}) assert_quantity_allclose(out[0], 15 * u.deg) assert_quantity_allclose(out[1], 15 * u.deg)
def create_slit(model, x0, y0, order): """ Create a SlitModel representing a grism slit.""" ymin = 0 xmin = 0 # ymax = 58 # xmax = 1323 model = Mapping((0, 1, 0, 0, 0)) | (Shift(xmin) & Shift(ymin) & Const1D(x0) & Const1D(y0) & Const1D(order)) | model wcsobj = wcs.WCS([('det', model), ('world', None)]) wcsobj.bounding_box = ((20, 25), (800, 805)) slit = SlitModel() slit.meta.wcs = wcsobj slit.source_xpos = x0 slit.source_ypos = y0 return slit
def test_levmar2x2_multivariate(): inputs = [np.array([10., 10., 20., 20.]), np.array([10., 20., 20., 10.])] outputs = [ np.array([8.06101731, 0.98994949, 8.06101731, 15.13208512]), np.array([12.16223664, 19.23330445, 26.30437226, 19.23330445]) ] rot = Rotation2D() rot.fittable = True model = (Shift() & Shift()) | rot fitter = linearfit._LevMarLSQFitter2x2() finfo = fitter(model, inputs, outputs) assert np.allclose(finfo.parameters, np.array([4.3, -7.1, 45.]), rtol=1e-5, atol=1e-5)
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 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 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 _convert_item_to_models(self, item, drop_all_non_separable): inputs = [] prepend = [] axes_to_drop = [] # Iterate over all the axes and keep a list of models prepend to the # transform, and a list of axes to remove from the wcs completely. # We always add a model to prepend list so that we maintain consistency # with the number of axes. If prepend is entirely identity models, it # is not used. input_units = self._input_units() for i, ax in enumerate(item): if isinstance(ax, int): if self.separable[i]: axes_to_drop.append(i) elif not self.separable[i] and drop_all_non_separable: axes_to_drop.append(i) else: inputs.append(ax * input_units[i]) prepend.append(Identity(1)) elif ax.start: inputs.append(None) prepend.append(Shift(ax.start * input_units[i])) else: inputs.append(None) prepend.append(Identity(1)) return inputs, prepend, axes_to_drop
def offset_wcs(slit_wcs): """ Prepend a Shift transform to the slit WCS to account for subarrays. Parameters ---------- slit_wcs : `~gwcs.wcs.WCS` The WCS for this slit. slit_name : str The name of the slit. """ xlo, xhi = _toindex(slit_wcs.bounding_box[0]) ylo, yhi = _toindex(slit_wcs.bounding_box[1]) # Add the slit offset to each slit WCS object tr = slit_wcs.get_transform('detector', 'sca') tr = Shift(xlo) & Shift(ylo) | tr slit_wcs.set_transform('detector', 'sca', tr.rename('dms2sca')) return xlo, xhi, ylo, yhi
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 # Generate grid of sky coordinates for area within domain x, y = wcstools.grid_from_domain(refwcs.domain) 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 domain dx0 = refwcs.domain[0]['lower'] dx1 = refwcs.domain[0]['upper'] dy0 = refwcs.domain[1]['lower'] dy1 = refwcs.domain[1]['upper'] 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 domain in the output frame wcs object domain_grid = wnew.backward_transform(ra, dec, lam) domain = [] for axis in input_frame.axes_order: axis_min = np.nanmin(domain_grid[axis]) axis_max = np.nanmax(domain_grid[axis]) + 1 domain.append({'lower': axis_min, 'upper': axis_max, 'includes_lower': True, 'includes_upper': False}) log.debug('Domain: {0} {1}'.format(domain[0]['lower'], domain[0]['upper'])) log.debug('Domain: {0} {1}'.format(domain[1]['lower'], domain[1]['upper'])) wnew.domain = domain # Update class properties self.output_spatial_scale = spatial_scale self.output_spectral_scale = spectral_scale self.output_wcs = wnew