def test_prepare_outputs_sparse_grid(): """ Test to show that #11060 has been solved. """ shape = (3, 3) data = np.arange(np.product(shape)).reshape(shape) * u.m / u.s points_unit = u.pix points = [np.arange(size) * points_unit for size in shape] kwargs = { 'bounds_error': False, 'fill_value': np.nan, 'method': 'nearest', } transform = models.Tabular2D(points, data, **kwargs) truth = np.array([[0., 1., 2.], [3., 4., 5.], [6., 7., 8.]]) * u.m / u.s points = np.meshgrid(np.arange(3), np.arange(3), indexing='ij', sparse=True) x = points[0] * u.pix y = points[1] * u.pix value = transform(x, y) assert (value == truth).all() points = np.meshgrid(np.arange(3), np.arange(3), indexing='ij', sparse=False) * u.pix value = transform(*points) assert (value == truth).all()
def test_tabular_str(): points = np.arange(5) lt = np.arange(5) t = models.Tabular1D(points, lt) assert str(t) == ("Model: Tabular1D\n" "N_inputs: 1\n" "N_outputs: 1\n" "Parameters: \n" " points: (array([0, 1, 2, 3, 4]),)\n" " lookup_table: [0 1 2 3 4]\n" " method: linear\n" " fill_value: nan\n" " bounds_error: True") table = np.arange(5 * 5).reshape(5, 5) points = np.arange(0, 5) points = (points, points) t = models.Tabular2D(points=points, lookup_table=table) assert str(t) == ( "Model: Tabular2D\n" "N_inputs: 2\n" "N_outputs: 1\n" "Parameters: \n" " points: (array([0, 1, 2, 3, 4]), array([0, 1, 2, 3, 4]))\n" " lookup_table: [[ 0 1 2 3 4]\n" " [ 5 6 7 8 9]\n" " [10 11 12 13 14]\n" " [15 16 17 18 19]\n" " [20 21 22 23 24]]\n" " method: linear\n" " fill_value: nan\n" " bounds_error: True")
def test_tabular1d_inverse(): """Test that the Tabular1D inverse is defined""" points = np.arange(5) values = np.array([1.5, 3.4, 6.7, 7, 32]) t = models.Tabular1D(points, values) result = t.inverse((3.4, 6.7)) assert_allclose(result, np.array((1., 2.))) # Check that it works for descending values in lookup_table t2 = models.Tabular1D(points, values[::-1]) assert_allclose(t2.inverse.points[0], t2.lookup_table[::-1]) result2 = t2.inverse((7, 6.7)) assert_allclose(result2, np.array((1., 2.))) # Check that it errors on double-valued lookup_table points = np.arange(5) values = np.array([1.5, 3.4, 3.4, 32, 25]) t = models.Tabular1D(points, values) with pytest.raises(NotImplementedError): t.inverse((3.4, 7.)) # Check that Tabular2D.inverse raises an error table = np.arange(5 * 5).reshape(5, 5) points = np.arange(0, 5) points = (points, points) t3 = models.Tabular2D(points=points, lookup_table=table) with pytest.raises(NotImplementedError): t3.inverse((3, 3))
def test_tabular_grid_shape_mismatch_error(): points = np.arange(5) lt = np.mgrid[0:5, 0:5][0] with pytest.raises(ValueError) as err: models.Tabular2D(points, lt) assert str(err.value) ==\ "Expected grid points in 2 directions, got 5."
def test_tabular_2d_quantity(): shape = (3, 3) data = np.arange(np.product(shape)).reshape(shape) * u.m / u.s # The integer location is at the centre of the pixel. points_unit = u.pix points = [(np.arange(size) - 0) * points_unit for size in shape] kwargs = { 'bounds_error': False, 'fill_value': np.nan, 'method': 'nearest', } forward = models.Tabular2D(points, data, **kwargs) input_frame = cf.CoordinateFrame(2, ("PIXEL", "PIXEL"), (0, 1), unit=(u.pix, u.pix), name="detector") output_frame = cf.CoordinateFrame(1, "CUSTOM", (0, ), unit=(u.m / u.s, )) w = wcs.WCS(forward_transform=forward, input_frame=input_frame, output_frame=output_frame) bb = w.bounding_box assert all(u.allclose(u.Quantity(b), [0, 2] * u.pix) for b in bb)
def test_tabular_interp_2d(): table = np.array( [[-0.04614432, -0.02512547, -0.00619557, 0.0144165, 0.0297525], [-0.04510594, -0.03183369, -0.01118008, 0.01201388, 0.02496205], [-0.05464094, -0.02804499, -0.00960086, 0.01134333, 0.02284104], [-0.04879338, -0.02539565, -0.00440462, 0.01795145, 0.02122417], [-0.03637372, -0.01630025, -0.00157902, 0.01649774, 0.01952131]]) points = np.arange(0, 5) points = (points, points) xnew = np.array([0., .7, 1.4, 2.1, 3.9]) LookupTable = models.tabular_model(2) model = LookupTable(points, table) znew = model(xnew, xnew) result = np.array( [-0.04614432, -0.03450009, -0.02241028, -0.0069727, 0.01938675]) assert_allclose(znew, result, atol=1e-7) # test 2D arrays as input a = np.arange(12).reshape((3, 4)) y, x = np.mgrid[:3, :4] t = models.Tabular2D(lookup_table=a) r = t(y, x) assert_allclose(a, r) with pytest.raises(ValueError): model = LookupTable(points=([1.2, 2.3], [1.2, 6.7], [3, 4])) with pytest.raises(ValueError): model = LookupTable(lookup_table=[1, 2, 3]) with pytest.raises(NotImplementedError): model = LookupTable(n_models=2) with pytest.raises(ValueError): model = LookupTable(([1, 2], [3, 4]), [5, 6]) with pytest.raises(ValueError): model = LookupTable(([1, 2] * u.m, [3, 4]), [[5, 6], [7, 8]]) with pytest.raises(ValueError): model = LookupTable(points, table, bounds_error=False, fill_value=1 * u.Jy) # Test unit support points = points[0] * u.nm points = (points, points) xnew = xnew * u.nm model = LookupTable(points, table * u.nJy) result = result * u.nJy assert_quantity_allclose(model(xnew, xnew), result, atol=1e-7 * u.nJy) xnew = xnew.to(u.m) assert_quantity_allclose(model(xnew, xnew), result, atol=1e-7 * u.nJy) bbox = (0 * u.nm, 4 * u.nm) bbox = (bbox, bbox) assert model.bounding_box == bbox
def test_tabular_model(tmpdir): points = np.arange(0, 5) values = [1., 10, 2, 45, -3] model = astmodels.Tabular1D(points=points, lookup_table=values) tree = {'model': model} helpers.assert_roundtrip_tree(tree, tmpdir) table = np.array([[ 3., 0., 0.], [ 0., 2., 0.], [ 0., 0., 0.]]) points = ([1, 2, 3], [1, 2, 3]) model2 = astmodels.Tabular2D(points, lookup_table=table, bounds_error=False, fill_value=None, method='nearest') tree = {'model': model2} helpers.assert_roundtrip_tree(tree, tmpdir)
def test_tabular_repr(): points = np.arange(5) lt = np.arange(5) t = models.Tabular1D(points, lt) assert repr(t) ==\ "<Tabular1D(points=(array([0, 1, 2, 3, 4]),), lookup_table=[0 1 2 3 4])>" table = np.arange(5 * 5).reshape(5, 5) points = np.arange(0, 5) points = (points, points) t = models.Tabular2D(points=points, lookup_table=table) assert repr(t) ==\ "<Tabular2D(points=(array([0, 1, 2, 3, 4]), array([0, 1, 2, 3, 4])), " +\ "lookup_table=[[ 0 1 2 3 4]\n" +\ " [ 5 6 7 8 9]\n" +\ " [10 11 12 13 14]\n" +\ " [15 16 17 18 19]\n" +\ " [20 21 22 23 24]])>"
def _wzpc2asdf(wzpcfile, author, description, useafter): f = fits.open(wzpcfile) width = f[1].header['width'] name = ap_names_map[f[0].header['COMPNAME']] data = f[1].data var = f[2].data w = wcs.WCS(f[1].header) f.close() y, x = np.mgrid[:data.shape[0], :data.shape[1]] # The WCS in the current ref files is 0-based. X, Y = w.all_pix2world(x, y, 0) tab = models.Tabular2D(points=(X[0], Y[:, 0]), lookup_table=data) aperture = { 'aperture_name': name, 'variance': var, 'zero_point_offset': tab, 'width': width } return aperture
result = model([1, 1], [2, 2]) assert_allclose(result, (np.array([1, 1]), np.array([2, 2]))) def test_format_input_arrays_transposed(): model = TInputFormatter() input = np.array([[1, 1]]).T, np.array([[2, 2]]).T result = model(*input) assert_allclose(result, input) @pytest.mark.parametrize('model', [ models.Gaussian2D(), models.Polynomial2D(1, ), models.Pix2Sky_TAN(), models.Tabular2D(lookup_table=np.ones((4, 5))) ]) @pytest.mark.skipif('not HAS_SCIPY') def test_call_keyword_args_2(model): """ Test calling a model with positional, keywrd and a mixture of both arguments. """ positional = model(1, 2) assert_allclose(positional, model(x=1, y=2)) assert_allclose(positional, model(1, y=2)) model.inputs = ('r', 't') assert_allclose(positional, model(r=1, t=2)) assert_allclose(positional, model(1, t=2)) assert_allclose(positional, model(1, 2))
def xytov2v3lam(xin, yin, stype): # Open relevant distortion file specfile = fits.open(get_fitsreffile()) # Convert input x,y vectors to numpy arrays x = np.array(xin) y = np.array(yin) # Global header hdr = specfile[0].header # File data lrsdata = np.array([l for l in specfile[1].data]) # Define zero points (in subarray frame), subarray location, subarray size if (stype.lower() == 'slit'): xsub0, ysub0 = 0, 0 xsub1, ysub1 = 1031, 1023 zero_point = hdr['imx'] - 1, hdr['imy'] - 1 elif (stype.lower() == 'slitless'): xsub0, ysub0 = 0, 528 xsub1, ysub1 = 71, 415 zero_point = hdr['imxsltl'] - 1 - xsub0, hdr['imysltl'] - 1 - ysub0 else: print('Invalid operation type: specify either slit or slitless') # In the lrsdata reference table, X_center,y_center,wavelength describe the location of the # centroid trace along the detector in pixels relative to nominal location. # The box corners for this wavelength are x0,y0(ul) x1,y1 (ur) x2,y2(lr) x3,y3(ll) # Use these to create the bounding box for all valid detector locations in units of subarray pixels xcen = lrsdata[:, 0] ycen = lrsdata[:, 1] wavetab = lrsdata[:, 2] x0 = lrsdata[:, 3] y0 = lrsdata[:, 4] x1 = lrsdata[:, 5] y2 = lrsdata[:, 8] bb = ((x0.min() - 0.5 + zero_point[0], x1.max() + 0.5 + zero_point[0]), (y2.min() - 0.5 + zero_point[1], y0.max() + 0.5 + zero_point[1])) # Find the ROW of the zero point row_zero_point = zero_point[1] # Make a vector of x,y locations for every pixel in the reference row yrow, xrow = np.mgrid[row_zero_point:row_zero_point + 1, 0:xsub1 + 1] # And compute the v2,v3 coordinates of pixels in this reference row v2refrow, v3refrow = mt.xytov2v3(xrow + xsub0, yrow + ysub0, 'F770W') # Now repeat the v2,v3, matrix from the central row so that it is copied to all of the other valid rows too v2_full = mb.repmat(v2refrow, np.int(bb[1][1]) + 1 - np.int(bb[1][0]), 1) v3_full = mb.repmat(v3refrow, np.int(bb[1][1]) + 1 - np.int(bb[1][0]), 1) # v2_full and v3_full now have shape (e.g. for slitless) 391x72 # Now take these matrices and put them into tabular models that can be interpolated to find v2,v3 for arbitrary # x,y pixel values in the valid region. v2_t2d = models.Tabular2D(lookup_table=v2_full, name='v2table', bounds_error=False, fill_value=np.nan) v3_t2d = models.Tabular2D(lookup_table=v3_full, name='v3table', bounds_error=False, fill_value=np.nan) # Now deal with the fact that the spectral trace isn't perfectly up and down along detector. # This information is contained in the xcenter/ycenter values in the CDP table, but we'll handle it # as a simple rotation using a linear fit to this relation provided by the CDP. z = np.polyfit(xcen, ycen, 1) slope = 1. / z[0] traceangle = np.arctan(slope) * 180. / np.pi # trace angle in degrees rot = models.Rotation2D(traceangle) # Rotation model # Now include this rotation in our overall transform # First shift to a frame relative to the trace zeropoint, then apply the rotation # to correct for the curved trace. End in a rotated frame relative to zero at the reference point # and where yrot is aligned with the spectral trace) xysubtoxyrot = models.Shift(-zero_point[0]) & models.Shift( -zero_point[1]) | rot # Next shift back to the subarray frame, and then map to v2v3 xyrottov2v3 = models.Shift(zero_point[0]) & models.Shift( zero_point[1]) | models.Mapping((1, 0, 1, 0)) | v2_t2d & v3_t2d # Compute the rotated x,y points for our inputs xrot, yrot = xysubtoxyrot(x, y) # Compute the v2,v3 points for our inputs v2, v3 = xyrottov2v3(xrot, yrot) # Work out the spectral component of the transform # First compute the reference trace in the rotated-Y frame xcenrot, ycenrot = rot(xcen, ycen) # The input table of wavelengths isn't perfect, and the delta-wavelength steps show some unphysical behaviour # Therefore fit with a spline for the ycenrot->wavelength transform # Reverse vectors so that yinv is increasing (needed for spline fitting function) wavetab = lrsdata[:, 2] yrev = ycenrot[::-1] wrev = wavetab[::-1] # Spline fit with enforced smoothness spl = UnivariateSpline(yrev, wrev, s=0.002) # Evaluate the fit at the rotated-y reference points waves = spl(yrot) return v2, v3, waves
def test_format_input_arrays(): model = TInputFormatter() result = model([1, 1], [2, 2]) assert_allclose(result, (np.array([1, 1]), np.array([2, 2]))) def test_format_input_arrays_transposed(): model = TInputFormatter() input = np.array([[1, 1]]).T, np.array([[2, 2]]).T result = model(*input) assert_allclose(result, input) @pytest.mark.parametrize('model', [models.Gaussian2D(), models.Polynomial2D(1,), models.Pix2Sky_TAN(), models.Tabular2D(lookup_table=np.ones((4,5)))]) @pytest.mark.skipif('not HAS_SCIPY') def test_call_keyword_args_2(model): """ Test calling a model with positional, keywrd and a mixture of both arguments. """ positional = model(1, 2) assert_allclose(positional, model(x=1, y=2)) assert_allclose(positional, model(1, y=2)) model.inputs = ('r', 't') assert_allclose(positional, model(r=1, t=2)) assert_allclose(positional, model(1, t=2)) assert_allclose(positional, model(1, 2)) with pytest.raises(ValueError):
def lrs(input_model, reference_files): """ The LRS-FIXEDSLIT and LRS-SLITLESS WCS pipeline. It has two coordinate frames: "detecor" and "world". Uses the "specwcs" and "distortion" reference files. """ # Setup the frames. detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) spec = cf.SpectralFrame(name='wavelength', axes_order=(2, ), unit=(u.micron, ), axes_names=('lambda', )) sky = cf.CelestialFrame(reference_frame=coord.ICRS(), name='sky') world = cf.CompositeFrame(name="world", frames=[sky, spec]) # Determine the distortion model. subarray2full = subarray_transform(input_model) with DistortionModel(reference_files['distortion']) as dist: distortion = dist.model full_distortion = subarray2full | distortion # Load and process the reference data. with fits.open(reference_files['specwcs']) as ref: lrsdata = np.array([l for l in ref[1].data]) # Get the zero point from the reference data. # The zero_point is X, Y (which should be COLUMN, ROW) # TODO: Are imx, imy 0- or 1-indexed? We are treating them here as # 0-indexed. Since they are FITS, they are probably 1-indexed. 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['imxsltl'], ref[1].header['imysltl'] zero_point = [35, 442] # [35, 763] # account for subarray # Create the bounding_box x0 = lrsdata[:, 3] y0 = lrsdata[:, 4] x1 = lrsdata[:, 5] bb = ((x0.min() - 0.5 + zero_point[0], x1.max() + 0.5 + zero_point[0]), (y0.min() - 0.5 + zero_point[1], y0.max() + 0.5 + zero_point[1])) # Find the ROW of the zero point which should be the [1] of zero_point row_zero_point = zero_point[1] # Compute the v2v3 to sky. tel2sky = pointing.v23tosky(input_model) # Compute the V2/V3 for each pixel in this row # x.shape will be something like (1, 388) y, x = np.mgrid[row_zero_point:row_zero_point + 1, 0:input_model.data.shape[1]] spatial_transform = full_distortion | tel2sky radec = np.array(spatial_transform(x, y))[:, 0, :] ra_full = np.matlib.repmat(radec[0], _toindex(bb[1][1]) + 1 - _toindex(bb[1][0]), 1) dec_full = np.matlib.repmat(radec[1], _toindex(bb[1][1]) + 1 - _toindex(bb[1][0]), 1) ra_t2d = models.Tabular2D(lookup_table=ra_full, name='xtable', bounds_error=False, fill_value=np.nan) dec_t2d = models.Tabular2D(lookup_table=dec_full, name='ytable', bounds_error=False, fill_value=np.nan) # Create the model transforms. lrs_wav_model = jwmodels.LRSWavelength(lrsdata, zero_point) # Incorporate the small rotation angle = np.arctan(0.00421924) rot = models.Rotation2D(angle) radec_t2d = ra_t2d & dec_t2d | rot # Account for the subarray when computing spatial coordinates. xshift = -bb[0][0] yshift = -bb[1][0] det2world = models.Mapping((1, 0, 1, 0, 0, 1)) | models.Shift(yshift, name='yshift1') & \ models.Shift(xshift, name='xshift1') & \ models.Shift(yshift, name='yshift2') & models.Shift(xshift, name='xshift2') & \ models.Identity(2) | radec_t2d & lrs_wav_model det2world.bounding_box = bb[::-1] # Now the actual pipeline. pipeline = [(detector, det2world), (world, None)] return pipeline
def lrs(input_model, reference_files): """ Create the WCS pipeline for a MIRI fixed slit observation. Parameters ---------- input_model : `jwst.datamodels.ImagingModel` Data model. reference_files : dict Dictionary {reftype: reference file name}. reference_files = { "specwcs": 'MIRI_FM_MIRIMAGE_P750L_DISTORTION_04.02.00.fits' } """ # Setup the frames. detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) spec = cf.SpectralFrame(name='wavelength', axes_order=(2, ), unit=(u.micron, ), axes_names=('lambda', )) sky = cf.CelestialFrame(reference_frame=coord.ICRS(), name='sky') world = cf.CompositeFrame(name="world", frames=[sky, spec]) # Determine the distortion model. distortion = AsdfFile.open(reference_files['distortion']).tree['model'] # Distortion is in arcmin. Convert to degrees full_distortion = distortion | models.Scale(1 / 60.) & models.Scale( 1 / 60.) # Load and process the reference data. with fits.open(reference_files['specwcs']) as ref: lrsdata = np.array([l for l in ref[1].data]) # Get the zero point from the reference data. # The zero_point is X, Y (which should be COLUMN, ROW) # TODO: Are imx, imy 0- or 1-indexed? We are treating them here as # 0-indexed. Since they are FITS, they are probably 1-indexed. 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['imxsltl'], ref[1].header['imysltl'] zero_point = [35, 442] # [35, 763] # account for subarray # Create the domain x0 = lrsdata[:, 3] y0 = lrsdata[:, 4] x1 = lrsdata[:, 5] 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]) }] # Find the ROW of the zero point which should be the [1] of zero_point row_zero_point = zero_point[1] # Compute the v2v3 to sky. tel2sky = pointing.v23tosky(input_model) # Compute the V2/V3 for each pixel in this row # x.shape will be something like (1, 388) y, x = np.mgrid[row_zero_point:row_zero_point + 1, 0:input_model.data.shape[1]] spatial_transform = full_distortion | tel2sky radec = np.array(spatial_transform(x, y))[:, 0, :] ra_full = np.matlib.repmat(radec[0], domain[1]['upper'] + 1 - domain[1]['lower'], 1) dec_full = np.matlib.repmat(radec[1], domain[1]['upper'] + 1 - domain[1]['lower'], 1) ra_t2d = models.Tabular2D(lookup_table=ra_full, name='xtable', bounds_error=False, fill_value=np.nan) dec_t2d = models.Tabular2D(lookup_table=dec_full, name='ytable', bounds_error=False, fill_value=np.nan) # Create the model transforms. lrs_wav_model = jwmodels.LRSWavelength(lrsdata, zero_point) # Incorporate the small rotation angle = np.arctan(0.00421924) rot = models.Rotation2D(angle) radec_t2d = ra_t2d & dec_t2d | rot # Account for the subarray when computing spatial coordinates. xshift = -domain[0]['lower'] yshift = -domain[1]['lower'] det2world = models.Mapping((1, 0, 1, 0, 0, 1)) | models.Shift(yshift, name='yshift1') & \ models.Shift(xshift, name='xshift1') & \ models.Shift(yshift, name='yshift2') & models.Shift(xshift, name='xshift2') & \ models.Identity(2) | radec_t2d & lrs_wav_model det2world.meta['domain'] = domain # Now the actual pipeline. pipeline = [(detector, det2world), (world, None)] return pipeline
astropy_models.RotateNative2Celestial(5.63, -72.5, 180), astropy_models.RotateNative2Celestial(5.63 * u.deg, -72.5 * u.deg, 180 * u.deg), astropy_models.Rotation2D(angle=1.51), astropy_models.RotationSequence3D([1.2, 2.3, 3.4, .3], "xyzx"), astropy_models.SphericalRotationSequence([1.2, 2.3, 3.4, .3], "xyzy"), # astropy.modeling.tabular astropy_models.Tabular1D(points=np.arange(0, 5), lookup_table=[1., 10, 2, 45, -3]), astropy_models.Tabular1D(points=np.arange(0, 5) * u.pix, lookup_table=[1., 10, 2, 45, -3] * u.nm), astropy_models.Tabular2D( points=([1, 2, 3], [1, 2, 3]), lookup_table=np.arange(0, 9).reshape(3, 3), bounds_error=False, fill_value=None, method="nearest", ), astropy_models.Tabular2D( points=([1, 2, 3], [1, 2, 3]) * u.pix, lookup_table=np.arange(0, 9).reshape(3, 3) * u.nm, bounds_error=False, fill_value=None, method="nearest", ), ] if astropy.__version__ >= "4.1": SINGLE_MODELS.append(astropy_models.Plummer1D(mass=10.0, r_plum=5.0))