def ifu_msa_to_oteip(reference_files): """ Transform from the MSA frame to the OTEIP frame. Parameters ---------- reference_files: dict Dictionary with reference files returned by CRDS. Returns ------- model : `~astropy.modeling.core.Model` model. Transform from MSA to OTEIP. """ with AsdfFile.open(reference_files['fore']) as f: fore = f.tree['model'].copy() with AsdfFile.open(reference_files['ifufore']) as f: ifufore = f.tree['model'].copy() msa2fore_mapping = Mapping((0, 1, 2, 2)) msa2fore_mapping.inverse = Identity(3) ifu_fore_transform = ifufore & Identity(1) ifu_fore_transform.inverse = Mapping( (0, 1, 2, 2)) | ifufore.inverse & Identity(1) fore_transform = msa2fore_mapping | fore & Identity(1) return msa2fore_mapping | ifu_fore_transform | fore_transform
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 setup_class(cls): cls.model1D = Identity(n_inputs=1) cls.model2D = Identity(n_inputs=2) | Mapping((0, ), n_inputs=2) cls.model3D = Identity(n_inputs=3) | Mapping((0, ), n_inputs=3) cls.data = cls.x = cls.y = cls.z = np.linspace(0, 10, num=100) cls.lsq_exp = 0
def ifu(input_model, reference_files): """ IFU pipeline """ slits = np.arange(30) # Get the corrected disperser model disperser = get_disperser(input_model, reference_files['disperser']) # Get the default spectral order and wavelength range and record them in the model. sporder, wrange = get_spectral_order_wrange( input_model, reference_files['wavelengthrange']) input_model.meta.wcsinfo.waverange_start = wrange[0] input_model.meta.wcsinfo.waverange_end = wrange[1] input_model.meta.wcsinfo.spectral_order = sporder # DMS to SCA transform dms2detector = dms_to_sca(input_model) # DETECTOR to GWA transform det2gwa = Identity(2) & detector_to_gwa( reference_files, input_model.meta.instrument.detector, disperser) # GWA to SLIT gwa2slit = gwa_to_ifuslit(slits, disperser, wrange, sporder, reference_files) # SLIT to MSA transform slit2msa = ifuslit_to_msa(slits, reference_files) det, sca, gwa, slit_frame, msa_frame, oteip, v2v3, world = create_frames() if input_model.meta.instrument.filter != 'OPAQUE': # MSA to OTEIP transform msa2oteip = ifu_msa_to_oteip(reference_files) # OTEIP to V2,V3 transform # This includes a wavelength unit conversion from meters to microns. oteip2v23 = oteip_to_v23(reference_files) # Create coordinate frames in the NIRSPEC WCS pipeline" # # The oteip2v2v3 transform converts the wavelength from meters (which is assumed # in the whole pipeline) to microns (which is the expected output) # # "detector", "gwa", "slit_frame", "msa_frame", "oteip", "v2v3", "world" pipeline = [(det, dms2detector), (sca, det2gwa.rename('detector2gwa')), (gwa, gwa2slit.rename('gwa2slit')), (slit_frame, (Mapping( (0, 1, 2, 3, 4)) | slit2msa).rename('slit2msa')), (msa_frame, msa2oteip.rename('msa2oteip')), (oteip, oteip2v23.rename('oteip2v23')), (v2v3, None)] else: pipeline = [(det, dms2detector), (sca, det2gwa.rename('detector2gwa')), (gwa, gwa2slit.rename('gwa2slit')), (slit_frame, (Mapping( (0, 1, 2, 3, 4)) | slit2msa).rename('slit2msa')), (msa_frame, None)] return pipeline
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 gwa_to_ifuslit(slits, disperser, wrange, order, reference_files): """ GWA to SLIT transform. Parameters ---------- slits : list A list of slit IDs for all IFU slits 0-29. disperser : dict A corrected disperser ASDF object. filter : str The filter used. grating : str The grating used in the observation. reference_files: dict Dictionary with reference files returned by CRDS. Returns ------- model : `~jwst_lib.pipeline_models.Gwa2Slit` model. Transform from GWA frame to SLIT frame. """ agreq = AngleFromGratingEquation(disperser['groove_density'], order, name='alpha_from_greq') lgreq = WavelengthFromGratingEquation(disperser['groove_density'], order, name='lambda_from_greq') collimator2gwa = collimator_to_gwa(reference_files, disperser) lam = (wrange[1] - wrange[0]) / 2 + wrange[0] ifuslicer = AsdfFile.open(reference_files['ifuslicer']) ifupost = AsdfFile.open(reference_files['ifupost']) slit_models = {} ifuslicer_model = ifuslicer.tree['model'] for slit in slits: slitdata = ifuslicer.tree['data'][slit] slitdata_model = get_slit_location_model(slitdata) ifuslicer_transform = (slitdata_model | ifuslicer_model) ifupost_transform = ifupost.tree[slit]['model'] msa2gwa = ifuslicer_transform | ifupost_transform | collimator2gwa gwa2msa = gwa_to_ymsa(msa2gwa) # TODO: Use model sets here bgwa2msa = Mapping((0, 1, 0, 1), n_inputs=3) | \ Const1D(0) * Identity(1) & Const1D(-1) * Identity(1) & Identity(2) | \ Identity(1) & gwa2msa & Identity(2) | \ Mapping((0, 1, 0, 1, 2, 3)) | Identity(2) & msa2gwa & Identity(2) | \ Mapping((0, 1, 2, 5), n_inputs=7)| Identity(2) & lgreq # msa to before_gwa #msa2bgwa = Mapping((0, 1, 2, 2)) | msa2gwa & Identity(1) | Mapping((3, 0, 1, 2)) | agreq msa2bgwa = msa2gwa & Identity(1) | Mapping((3, 0, 1, 2)) | agreq bgwa2msa.inverse = msa2bgwa slit_models[slit] = bgwa2msa ifuslicer.close() ifupost.close() return Gwa2Slit(slit_models)
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 gwa_to_slit(slits_id, disperser, wrange, order, reference_files): """ GWA to SLIT transform. Parameters ---------- slits_id : list A list of slit IDs for all open shutters/slitlets. disperser : dict A corrected disperser ASDF object. filter : str The filter used. grating : str The grating used in the observation. reference_files: dict Dictionary with reference files returned by CRDS. Returns ------- model : `~jwst_lib.pipeline_models.Gwa2Slit` model. Transform from GWA frame to SLIT frame. """ agreq = AngleFromGratingEquation(disperser['groove_density'], order, name='alpha_from_greq') lgreq = WavelengthFromGratingEquation(disperser['groove_density'], order, name='lambda_from_greq') collimator2gwa = collimator_to_gwa(reference_files, disperser) msa = AsdfFile.open(reference_files['msa']) slit_models = {} for i in range(1, 6): slit_names = slits_id[slits_id[:, 0] == i] if slit_names.any(): msa_model = msa.tree[i]['model'] for slit in slit_names: index = slit[1] - 1 slitdata = msa.tree[slit[0]]['data'][index] slitdata_model = get_slit_location_model(slitdata) msa_transform = slitdata_model | msa_model msa2gwa = (msa_transform | collimator2gwa) gwa2msa = gwa_to_ymsa(msa2gwa) # TODO: Use model sets here bgwa2msa = Mapping((0, 1, 0, 1), n_inputs=3) | \ Const1D(0) * Identity(1) & Const1D(-1) * Identity(1) & Identity(2) | \ Identity(1) & gwa2msa & Identity(2) | \ Mapping((0, 1, 0, 1, 2, 3)) | Identity(2) & msa2gwa & Identity(2) | \ Mapping((0, 1, 2, 5), n_inputs=7)| Identity(2) & lgreq # msa to before_gwa msa2bgwa = msa2gwa & Identity(1) | Mapping( (3, 0, 1, 2)) | agreq bgwa2msa.inverse = msa2bgwa s = slitid_to_slit(np.array([slit]))[0] slit_models[s] = bgwa2msa msa.close() return Gwa2Slit(slit_models)
def imaging(input_model, reference_files): """ Imaging pipeline frames : detector, gwa, msa, sky """ # Get the corrected disperser model disperser = get_disperser(input_model, reference_files['disperser']) # DETECTOR to GWA transform det2gwa = detector_to_gwa(reference_files, input_model.meta.instrument.detector, disperser) gwa_through = Const1D(-1) * Identity(1) & Const1D(-1) * Identity( 1) & Identity(1) angles = [ disperser['theta_x'], disperser['theta_y'], disperser['theta_z'], disperser['tilt_y'] ] rotation = Rotation3DToGWA(angles, axes_order="xyzy", name='rotaton').inverse dircos2unitless = DirCos2Unitless(name='directional_cosines2unitless') col = AsdfFile.open(reference_files['collimator']).tree['model'] # Get the default spectral order and wavelength range and record them in the model. sporder, wrange = get_spectral_order_wrange( input_model, reference_files['wavelengthrange']) input_model.meta.wcsinfo.waverange_start = wrange[0] input_model.meta.wcsinfo.waverange_end = wrange[1] input_model.meta.wcsinfo.spectral_order = sporder lam = wrange[0] + (wrange[1] - wrange[0]) * .5 lam_model = Mapping((0, 1, 1)) | Identity(2) & Const1D(lam) gwa2msa = gwa_through | rotation | dircos2unitless | col | lam_model gwa2msa.inverse = col.inverse | dircos2unitless.inverse | rotation.inverse | gwa_through # MSA to OTEIP transform msa2ote = msa_to_oteip(reference_files) msa2oteip = msa2ote | Mapping((0, 1), n_inputs=3) msa2oteip.inverse = Mapping((0, 1, 0, 1)) | msa2ote.inverse | Mapping( (0, 1), n_inputs=3) # OTEIP to V2,V3 transform with AsdfFile.open(reference_files['ote']) as f: oteip2v23 = f.tree['model'].copy() # Create coordinate frames in the NIRSPEC WCS pipeline # "detector", "gwa", "msa", "oteip", "v2v3", "world" det, gwa, msa_frame, oteip, v2v3 = create_imaging_frames() imaging_pipeline = [(det, det2gwa), (gwa, gwa2msa), (msa_frame, msa2oteip), (oteip, oteip2v23), (v2v3, None)] return imaging_pipeline
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_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 _v2v3_to_tpcorr_from_full(tpcorr): s2c = tpcorr['s2c'] c2s = tpcorr['c2s'] # unit_conv = _get_submodel(tpcorr, 'arcsec_to_deg_2D') # unit_conv_inv = _get_submodel(tpcorr, 'deg_to_arcsec_2D') # # The code below is a work-around to the code above not working. # TODO: understand why _get_submodel is unable to retrieve # some submodels in a compound model. # unit_conv = _get_submodel(tpcorr, 'arcsec_to_deg_1D') unit_conv = unit_conv & unit_conv unit_conv.name = 'arcsec_to_deg_2D' unit_conv_inv = _get_submodel(tpcorr, 'deg_to_arcsec_1D') unit_conv_inv = unit_conv_inv & unit_conv_inv unit_conv_inv.name = 'deg_to_arcsec_2D' affine = tpcorr['tp_affine'] affine_inv = affine.inverse affine_inv.name = 'tp_affine_inv' rot = tpcorr['det_to_optic_axis'] rot_inv = rot.inverse rot_inv.name = 'optic_axis_to_det' # c2tan = _get_submodel(tpcorr, 'Cartesian 3D to TAN') # tan2c = _get_submodel(tpcorr, 'TAN to cartesian 3D') # # The code below is a work-around to the code above not working. # TODO: understand why _get_submodel is unable to retrieve # some submodels in a compound model. # 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' v2v3_to_tpcorr = unit_conv | s2c | rot | c2tan | affine v2v3_to_tpcorr.name = 'jwst_v2v3_to_tpcorr' tpcorr_to_v2v3 = affine_inv | tan2c | rot_inv | c2s | unit_conv_inv tpcorr_to_v2v3.name = 'jwst_tpcorr_to_v2v3' v2v3_to_tpcorr.inverse = tpcorr_to_v2v3 tpcorr_to_v2v3.inverse = v2v3_to_tpcorr return v2v3_to_tpcorr
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 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_LabelMapperDict(tmpdir): dmapper = create_scalar_mapper() sel = selector.LabelMapperDict(('x', 'y'), dmapper, inputs_mapping=Mapping((0, ), n_inputs=2), atol=1e-3) tree = {'model': sel} helpers.assert_roundtrip_tree(tree, tmpdir, extensions=GWCSExtension())
def niriss_soss(input_model, reference_files): """ The NIRISS SOSS pipeline includes 3 coordinate frames - detector, focal plane and sky reference_files={'specwcs': 'soss_wavelengths_configuration.asdf'} """ # Get the target RA and DEC, they will be used for setting the WCS RA and DEC based on a conversation # with Kevin Volk. try: target_ra = float(input_model['meta.target.ra']) target_dec = float(input_model['meta.target.dec']) except: # There was an error getting the target RA and DEC, so we are not going to continue. raise ValueError('Problem getting the TARG_RA or TARG_DEC from input model {}'.format(input_model)) # Define the frames detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) spec = cf.SpectralFrame(name='spectral', axes_order=(2,), unit=(u.micron,), axes_names=('wavelength',)) sky = cf.CelestialFrame(reference_frame=coord.ICRS(), axes_names=('ra', 'dec'), axes_order=(0, 1), unit=(u.deg, u.deg), name='sky') world = cf.CompositeFrame([sky, spec], name='world') try: with AsdfFile.open(reference_files['specwcs']) as wl: wl1 = wl.tree[1].copy() wl2 = wl.tree[2].copy() wl3 = wl.tree[3].copy() except Exception as e: raise IOError('Error reading wavelength correction from {}'.format(reference_files['specwcs'])) cm_order1 = (Mapping((0, 1, 0, 1)) | (Const1D(target_ra) & Const1D(target_dec) & wl1)).rename('Order1') cm_order2 = (Mapping((0, 1, 0, 1)) | (Const1D(target_ra) & Const1D(target_dec) & wl2)).rename('Order2') cm_order3 = (Mapping((0, 1, 0, 1)) | (Const1D(target_ra) & Const1D(target_dec) & wl3)).rename('Order3') # Define the transforms, they should accept (x,y) and return (ra, dec, lambda) soss_model = NirissSOSSModel([1, 2, 3], [cm_order1, cm_order2, cm_order3]).rename('3-order SOSS Model') # Define the pipeline based on the frames and models above. pipeline = [(detector, soss_model), (world, None) ] return pipeline
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 test_swap_axes(): x = np.zeros((2, 3)) y = np.ones((2, 3)) mapping = Mapping((1, 0)) assert mapping(1, 2) == (2.0, 1.0) assert mapping.inverse(2, 1) == (1, 2) assert_array_equal(mapping(x, y), (y, x)) assert_array_equal(mapping.inverse(y, x), (x, y))
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 from_tree_transform(cls, node, ctx): mapping = node['mapping'] n_inputs = node.get('n_inputs') if all([isinstance(x, int) for x in mapping]): return Mapping(tuple(mapping), n_inputs) if n_inputs is None: n_inputs = max([x for x in mapping if isinstance(x, int)]) + 1 transform = Identity(n_inputs) new_mapping = [] i = n_inputs for entry in mapping: if isinstance(entry, int): new_mapping.append(entry) else: new_mapping.append(i) transform = transform & Const1D(entry.value) i += 1 return transform | Mapping(new_mapping)
def test_cdot(): result = _cdot(sh1, scl1) assert_allclose(result, np.array([[1]])) result = _cdot(rot, p2) assert_allclose(result, np.array([[2, 2]])) result = _cdot(rot, rot) assert_allclose(result, np.array([[2, 2], [2, 2]])) result = _cdot(Mapping((0, 0)), rot) assert_allclose(result, np.array([[2], [2]]))
def test_fittable_compound(): m = Identity(1) | Mapping((0, )) | Gaussian1D(1, 5, 4) x = np.arange(10) y_real = m(x) dy = 0.005 with NumpyRNGContext(1234567): n = np.random.normal(0., dy, x.shape) y_noisy = y_real + n pfit = LevMarLSQFitter() new_model = pfit(m, x, y_noisy) y_fit = new_model(x) assert_allclose(y_fit, y_real, atol=dy)
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 mask_slit(ymin=-.5, ymax=.5): """ Returns a model which masks out pixels in a NIRSpec cutout outside the slit. Uses ymin, ymax for the slit and the wavelength range to define the location of the slit. Parameters ---------- ymin, ymax : float ymin and ymax relative boundary of a slit. Returns ------- model : `~astropy.modeling.core.Model` A model which takes x_slit, y_slit, lam inputs and substitutes the values outside the slit with NaN. """ greater_than_ymax = Logical(condition='GT', compareto=ymax, value=np.nan) less_than_ymin = Logical(condition='LT', compareto=ymin, value=np.nan) model = Mapping((0, 1, 2, 1)) | Identity(3) & (greater_than_ymax | less_than_ymin | models.Scale(0)) | \ Mapping((0, 1, 3, 2, 3)) | Identity(1) & Mapping((0,), n_inputs=2) + Mapping((1,)) & \ Mapping((0,), n_inputs=2) + Mapping((1,)) model.inverse = Identity(3) return model
def slits_wcs(input_model, reference_files): """ Create the WCS pipeline for observations using the MSA shutter array or fixed slits. Parameters ---------- input_model : `~jwst_lib.models.ImageModel` The input data model. reference_files : dict Dictionary with reference files supplied by CRDS. """ open_slits_id = get_open_slits(input_model) n_slits = len(open_slits_id) log.info("Computing WCS for {0} open slitlets".format(n_slits)) # Get the corrected disperser model disperser = get_disperser(input_model, reference_files['disperser']) # Get the default spectral order and wavelength range and record them in the model. sporder, wrange = get_spectral_order_wrange( input_model, reference_files['wavelengthrange']) input_model.meta.wcsinfo.waverange_start = wrange[0] input_model.meta.wcsinfo.waverange_end = wrange[1] input_model.meta.wcsinfo.spectral_order = sporder # DETECTOR to GWA transform #det2gwa = Identity(2) & detector_to_gwa(reference_files, input_model.meta.instrument.detector, disperser) det2gwa = detector_to_gwa(reference_files, input_model.meta.instrument.detector, disperser) # GWA to SLIT gwa2slit = gwa_to_slit(open_slits_id, disperser, wrange, sporder, reference_files) # SLIT to MSA transform slit2msa = slit_to_msa(open_slits_id, reference_files['msa']) # MSA to OTEIP transform msa2oteip = msa_to_oteip(reference_files) # OTEIP to V2,V3 transform oteip2v23 = oteip_to_v23(reference_files) # Create coordinate frames in the NIRSPEC WCS pipeline" # "detector", "gwa", "slit_frame", "msa_frame", "oteip", "v2v3", "world" det, gwa, slit_frame, msa_frame, oteip, v2v3 = create_frames() msa_pipeline = [(det, det2gwa), (gwa, gwa2slit), (slit_frame, Mapping((0, 1, 2, 3, 4)) | slit2msa), (msa_frame, msa2oteip), (oteip, oteip2v23), (v2v3, None)] return msa_pipeline
def test_mapping_inverse(): """Tests inverting a compound model that includes a `Mapping`.""" RS = Rotation2D & Scale # 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 = RS | Mapping([2, 0, 1]) | RS m = M(12.1, 13.2, 14.3, 15.4) assert_allclose((0, 1, 2), m.inverse(*m(0, 1, 2)), atol=1e-08)
def test_bad_inputs(name): mapping = Mapping((1, 0), name=name) if name is None: name = "Mapping" x = [np.ones((2, 3)) * idx for idx in range(5)] for idx in range(1, 6): if idx == 2: continue with pytest.raises(TypeError) as err: mapping.evaluate(*x[:idx]) assert str(err.value) == f"{name} expects 2 inputs; got {idx}"
def from_tree_transform(cls, node, ctx): from astropy.modeling.models import Identity, Mapping mapping = node['mapping'] n_inputs = node.get('n_inputs') if all([isinstance(x, six.integer_types) for x in mapping]): return Mapping(tuple(mapping), n_inputs) if n_inputs is None: n_inputs = max([x for x in mapping if isinstance(x, six.integer_types)]) + 1 transform = Identity(n_inputs) new_mapping = [] i = n_inputs for entry in mapping: if isinstance(entry, six.integer_types): new_mapping.append(entry) else: new_mapping.append(i) transform = transform & ConstantType.from_tree( {'value': int(entry.value)}, ctx) i += 1 return transform | Mapping(new_mapping)
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_LabelMapperRange(tmpdir): m = [] for i in np.arange(9) * .1: c0_0, c1_0, c0_1, c1_1 = np.ones((4, )) * i m.append(Polynomial2D(2, c0_0=c0_0, c1_0=c1_0, c0_1=c0_1, c1_1=c1_1)) keys = np.array([[4.88, 5.64], [5.75, 6.5], [6.67, 7.47], [7.7, 8.63], [8.83, 9.96], [10.19, 11.49], [11.77, 13.28], [13.33, 15.34], [15.56, 18.09]]) rmapper = {} for k, v in zip(keys, m): rmapper[tuple(k)] = v sel = selector.LabelMapperRange(('x', 'y'), rmapper, inputs_mapping=Mapping((0, ), n_inputs=2)) tree = {'model': sel} helpers.assert_roundtrip_tree(tree, tmpdir, extensions=GWCSExtension())