def test_ShiftCoeffs(verbose=False): """ Test accuracy of shift_coefficients method""" # First invent a plausible polynomial order = 5 a = makeup_polynomial() if verbose: print('A') polynomial.print_triangle(a) # Shift by a random step np.random.seed(seed=1) [xshift, yshift] = 1024.0 * np.random.rand(2) - 512.0 ashift = polynomial.shift_coefficients(a, xshift, yshift, verbose) if verbose: print('AS') polynomial.print_triangle(ashift) # Choose a random point [x, y] = 2048 * np.random.rand(2) - 1024.0 u1 = polynomial.poly(a, x, y, order) u2 = polynomial.poly(ashift, x - xshift, y - yshift, order) if verbose: print('XY', x, y) print('Shift', xshift, yshift) print('U values', u1, u2, u1 - u2) assert abs(u1 - u2) < 1.0e-12, 'Inaccurate shift transformation' return None
detector_layout_index = detector_layout['AperName'].tolist().index( master_aperture_name) for attribute in 'DetSciYAngle DetSciParity VIdlParity'.split(): setattr(aperture, attribute, detector_layout[attribute][detector_layout_index]) # this is the name given to the pseudo-aperture in the Calc worksheet csv_aperture_name = 'DET_DMF' aperture.Sci2IdlDeg = polynomial_degree dx = aperture.XDetRef - csv_data[csv_aperture_name]['dx'] dy = aperture.YDetRef - csv_data[csv_aperture_name]['dy'] csv_data[csv_aperture_name][ 'A_shifted'] = polynomial.shift_coefficients( csv_data[csv_aperture_name]['A'], dx, dy, verbose=False) csv_data[csv_aperture_name][ 'B_shifted'] = polynomial.shift_coefficients( csv_data[csv_aperture_name]['B'], dx, dy, verbose=False) # apply polynomial to get reference location in ideal plane dxIdl = polynomial.poly(csv_data[csv_aperture_name]['A'], dx, dy, order=polynomial_degree) dyIdl = polynomial.poly(csv_data[csv_aperture_name]['B'], dx, dy, order=polynomial_degree) csv_data[csv_aperture_name][
def get_mirim_coefficients(distortion_file, verbose=False): """Read delivered FITS file for MIRI imager and return data to be ingested in SIAF. Parameters ---------- distortion_file : str Name of distortion file. verbose : bool verbosity Returns ------- csv_data : dict Dictionary containing the data """ miri = fits.open(os.path.join(source_data_dir, distortion_file)) T = miri['T matrix'].data TI = miri['TI matrix'].data # CDP7 T matrices transform from/to v2,v3 in arcsec # set VtoAN and ANtoV to unit matrix VtoAN = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]) ANtoV = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]) TV = np.dot(T, VtoAN) VT = np.dot(ANtoV, TI) prod = np.dot(VT, TV) TT = np.dot(T, TI) if verbose: print('T\n', T) print('TI\n', TI) print('VtoAN\n', VtoAN) print('\n TV V2V3 to XY Entrance') print(TV) print(1.0 / TV[1, 1], 'arcsec/mm') print('\nANtoV\n', ANtoV) print('\n VTXY entrance to V2V3') print('VT\n', VT) print() print('VT comparison\n', prod) print('T comparison\n', TT) # Get linear coefficient layout A = miri['AI matrix'].data B = miri['BI matrix'].data C = miri['A matrix'].data D = miri['B matrix'].data AL = untangle(A) BL = untangle(B) CL = untangle(C) DL = untangle(D) if verbose: print('Initial AL\n', AL) print('Initial BL\n', BL) print('CL\n', CL) print('DL\n', DL) # scale factor corresponding to 25 mum pixel size, i.e. 40 pixels/mm order = 4 k = 0 for i in range(order + 1): factor = 0.025**i for j in range(i + 1): AL[k] = AL[k] * factor BL[k] = BL[k] * factor k += 1 AF = VT[0, 0] * AL + VT[0, 1] * BL AF[0] = AF[0] + VT[0, 2] BF = VT[1, 0] * AL + VT[1, 1] * BL BF[0] = BF[0] + VT[1, 2] if verbose: polynomial.print_triangle(AF) polynomial.print_triangle(BF) print('AL scaled\n', AL) print('\n A FINAL') print('\n B FINAL') ## print('INVERSE TRANSFORMATIONS') # Combine TV with polynomial using polynomial.two_step # combination of several polynomial coefficients a = np.array([TV[0, 2], TV[0, 0], TV[0, 1]]) b = np.array([TV[1, 2], TV[1, 0], TV[1, 1]]) (C2, D2) = polynomial.two_step(CL, DL, a, b) CF = 40 * C2 DF = 40 * D2 if verbose: polynomial.print_triangle(CF) polynomial.print_triangle(DF) print('a', a) print('b', b) print('\nC Final') print('\nD Final') # if verbose: # Test two_step v2 = -280 v3 = -430 xin = TV[0, 0] * v2 + TV[0, 1] * v3 + TV[0, 2] yin = TV[1, 0] * v2 + TV[1, 1] * v3 + TV[1, 2] xmm = polynomial.poly(CL, xin, yin, 4) ymm = polynomial.poly(DL, xin, yin, 4) xmm2 = polynomial.poly(C2, v2, v3, 4) ymm2 = polynomial.poly(D2, v2, v3, 4) # Backwards check xp = 0 yp = 0 v2 = polynomial.poly(AF, xp, yp, 4) v3 = polynomial.poly(BF, xp, yp, 4) xpix = polynomial.poly(CF, v2, v3, 4) ypix = polynomial.poly(DF, v2, v3, 4) print('IN', xin, yin) print('MM', xmm, ymm) print('MM2', xmm2, ymm2) print('V', v2, v3) print('Original ', xp, yp) print('Recovered', xpix, ypix) print('Change ', xpix - xp, ypix - yp) invcheck(AF, BF, CF, DF, 4, -512.0, 512.0) CS = polynomial.shift_coefficients(CF, AF[0], BF[0]) DS = polynomial.shift_coefficients(DF, AF[0], BF[0]) CS[0] = 0.0 DS[0] = 0.0 # extract V2,V3 reference position V2cen = AF[0] V3cen = BF[0] # reset zero order coefficients to zero AF[0] = 0.0 BF[0] = 0.0 if verbose: polynomial.print_triangle(CS) polynomial.print_triangle(DS) invcheck(AF, BF, CS, DS, 4, -512.0, 512.0) print('\nCS') print('\nDS') print('\nDetector Center') # if verbose: xscalec = np.hypot(AF[1], BF[1]) yscalec = np.hypot(AF[2], BF[2]) # compute angles xanglec = np.rad2deg(np.arctan2(AF[1], BF[1])) yanglec = np.rad2deg(np.arctan2(AF[2], BF[2])) if verbose: print('Position', V2cen, V3cen) print('Scales %10.6f %10.6f' % (xscalec, yscalec)) print('Angles %10.6f %10.6f' % (xanglec, yanglec)) # if verbose: xcen = 1033 / 2 ycen = 1025 / 2 xref = 693.5 - xcen yref = 512.5 - ycen V2Ref = polynomial.poly(AF, xref, yref, 4) + V2cen V3Ref = polynomial.poly(BF, xref, yref, 4) + V3cen dV2dx = polynomial.dpdx(AF, xref, yref) dV3dx = polynomial.dpdx(BF, xref, yref) dV2dy = polynomial.dpdy(AF, xref, yref) dV3dy = polynomial.dpdy(BF, xref, yref) xangler = np.arctan2(dV2dx, dV3dx) yangler = np.arctan2(dV2dy, dV3dy) # if verbose: print('Axis angles', np.rad2deg(xangler), np.rad2deg(yangler)) # if verbose: # Illum reference position xscaler = np.hypot(dV2dx, dV3dx) yscaler = np.hypot(dV2dy, dV3dy) xangler = np.rad2deg(np.arctan2(dV2dx, dV3dx)) yangler = np.rad2deg(np.arctan2(dV2dy, dV3dy)) # if verbose: print('\nIllum reference position') print('xref=', xref) print('Position', V2Ref, V3Ref) print('Scales %10.6f %10.6f' % (xscaler, yscaler)) print('Angles %10.6f %10.6f %10.6f' % (xangler, yangler, yangler - xangler)) # if verbose: # Slit position xslit = (326.13) yslit = (300.70) dxslit = xslit - xcen dyslit = yslit - ycen V2slit = polynomial.poly(AF, dxslit, dyslit, 4) + V2cen V3slit = polynomial.poly(BF, dxslit, dyslit, 4) + V3cen dV2dx = polynomial.dpdx(AF, dxslit, yslit) dV3dx = polynomial.dpdx(BF, dxslit, dyslit) dV2dy = polynomial.dpdy(AF, dxslit, dyslit) dV3dy = polynomial.dpdy(BF, dxslit, dyslit) xangles = np.arctan2(dV2dx, dV3dx) yangles = np.arctan2(dV2dy, dV3dy) # if verbose: print('\nSlit') print('Position', dxslit, dyslit) print('V2,V3', V2slit, V3slit) print('Slit angles', np.rad2deg(xangles), np.rad2deg(yangles)) # if verbose: # Corners xc = np.array([-516.0, 516.0, 516.0, -516.0, -516.0]) yc = np.array([-512.0, -512.0, 512.0, 512.0, -512.0]) V2c = polynomial.poly(AF, xc, yc, 4) V3c = polynomial.poly(BF, xc, yc, 4) V2c = V2c + V2cen V3c = V3c + V3cen # if verbose: print('\nCorners') print('V2 %10.4f %10.4f %10.4f %10.4f' % (V2c[0], V2c[1], V2c[2], V2c[3])) print('V3 %10.4f %10.4f %10.4f %10.4f' % (V3c[0], V3c[1], V3c[2], V3c[3])) # make figure pl.figure(1) pl.clf() pl.title('MIRI Detector') pl.plot(V2cen, V3cen, 'r+') pl.plot(V2c, V3c, ':') pl.grid(True) pl.axis('equal') pl.plot(V2Ref, V3Ref, 'b+') pl.plot(V2slit, V3slit, 'c+') pl.gca().invert_xaxis() pl.show() ## Rotated versions print('Angle', yanglec) print('Rotated') # incorporate rotation in coefficients a = np.deg2rad(yanglec) AR = AF * np.cos(a) - BF * np.sin(a) BR = AF * np.sin(a) + BF * np.cos(a) CR = polynomial.prepend_rotation_to_polynomial(CS, yanglec) DR = polynomial.prepend_rotation_to_polynomial(DS, yanglec) if verbose: print('AR') polynomial.print_triangle(AR) print('BR') polynomial.print_triangle(BF) print('\n', AR[2], ' near zero') # if verbose: invcheck(AR, BR, CR, DR, 4, -512.0, 512.0) # Check positions using rotated (Ideal) coefficients # if verbose: xi = polynomial.poly(AR, xc, yc, 4) yi = polynomial.poly(BR, xc, yc, 4) v2r = xi * np.cos(a) + yi * np.sin(a) + V2cen v3r = -xi * np.sin(a) + yi * np.cos(a) + V3cen # if verbose: print('V2', v2r) print('V3', v3r) pl.plot(v2r, v3r, '--') CRFl = polynomial.flip_x(CR) DRFl = polynomial.flip_x(DR) # see TR: "polynomial origin being at the detector center with # pixel position (516.5, 512.5). " detector_center_pixel_x = 516.5 detector_center_pixel_y = 512.5 # dictionary holding data written to csv csv_data = {} csv_data['DET_OSS'] = {} csv_data['DET_OSS']['A'] = AR csv_data['DET_OSS']['B'] = BR csv_data['DET_OSS']['C'] = CR csv_data['DET_OSS']['D'] = DR csv_data['DET_OSS']['Xref'] = detector_center_pixel_x csv_data['DET_OSS']['Yref'] = detector_center_pixel_y csv_data['DET_OSS']['Xref_inv'] = V2cen csv_data['DET_OSS']['Yref_inv'] = V3cen csv_data['DET_OSS']['xAngle'] = xanglec csv_data['DET_OSS']['yAngle'] = yanglec csv_data['DET_DMF'] = {} csv_data['DET_DMF']['A'] = -AR csv_data['DET_DMF']['B'] = BR csv_data['DET_DMF']['C'] = CRFl csv_data['DET_DMF']['D'] = DRFl csv_data['DET_DMF']['Xref'] = detector_center_pixel_x csv_data['DET_DMF']['Yref'] = detector_center_pixel_y csv_data['DET_DMF']['Xref_inv'] = V2cen csv_data['DET_DMF']['Yref_inv'] = V3cen csv_data['DET_DMF']['xAngle'] = xanglec csv_data['DET_DMF']['yAngle'] = yanglec return csv_data
def process_nirspec_aperture(aperture, verbose=False): """Set aperture parameters for master apertures and FULLSCA and OSS apertures. Parameters ---------- aperture verbose Returns ------- """ AperName = aperture.AperName index = siaf_aperture_definitions['AperName'].tolist().index(AperName) parent_aperture_name = None if (siaf_aperture_definitions['parent_apertures'][index] is not None) and ( siaf_aperture_definitions['dependency_type'][index] == 'default'): aperture._parent_apertures = siaf_aperture_definitions[ 'parent_apertures'][index] parent_aperture = aperture_dict[aperture._parent_apertures] parent_aperture_name = parent_aperture.AperName for attribute in 'DetSciYAngle Sci2IdlDeg DetSciParity VIdlParity'.split( ): setattr(aperture, attribute, getattr(parent_aperture, attribute)) polynomial_degree = 5 aperture.Sci2IdlDeg = polynomial_degree if (AperName in ['NRS1_FULL', 'NRS1_FULL_OSS']) or (parent_aperture_name == 'NRS1_FULL'): pcf_name = '491_GWA' elif (AperName in ['NRS2_FULL', 'NRS2_FULL_OSS']) or (parent_aperture_name == 'NRS2_FULL'): pcf_name = '492_GWA' if parent_aperture_name is None: Xref = aperture.XDetRef Yref = aperture.YDetRef else: Xref = parent_aperture.XDetRef Yref = parent_aperture.YDetRef for axis in ['A', 'B']: # modified is _shifted or _XYflipped, see Calc worksheet Rows 8,9,10 pcf_data[pcf_name]['{}_modified'.format( axis)] = polynomial.shift_coefficients( pcf_data[pcf_name]['{}'.format(axis)], Xref, Yref, verbose=False) if (AperName in ['NRS2_FULL']) or (parent_aperture_name == 'NRS2_FULL'): # Add an XY flip (The definition of the SCI frame differs from that of the DET frame, # therefore the polynomial coefficients are redefined so the net transformation from # the DET to GWA plane is the same as is obtained when the NRS2_FULL_OSS row is used. # see JWST-STScI-005921.) pcf_data[pcf_name]['{}_modified'.format(axis)] = polynomial.FlipXY( pcf_data[pcf_name]['{}_modified'.format(axis)]) if 'MIMF' not in AperName: Xoffset = 0 Yoffset = 0 else: Xoffset = aperture.XSciRef - parent_aperture.XSciRef Yoffset = aperture.YSciRef - parent_aperture.YSciRef sci2idlx_coefficients = polynomial.shift_coefficients( pcf_data[pcf_name]['{}_modified'.format('A')], Xoffset, Yoffset, verbose=False) sci2idly_coefficients = polynomial.shift_coefficients( pcf_data[pcf_name]['{}_modified'.format('B')], Xoffset, Yoffset, verbose=False) # set polynomial coefficients for transformation that goes directly to the GWA pupil plane idl2sci_factor = +1 if (AperName in ['NRS2_FULL']) or ('NRS2_FP' in AperName): idl2sci_factor = -1 k = 0 for i in range(polynomial_degree + 1): for j in np.arange(i + 1): setattr(aperture, 'Sci2IdlX{:d}{:d}'.format(i, j), sci2idlx_coefficients[k]) setattr(aperture, 'Sci2IdlY{:d}{:d}'.format(i, j), sci2idly_coefficients[k]) setattr(aperture, 'Idl2SciX{:d}{:d}'.format(i, j), idl2sci_factor * pcf_data[pcf_name]['C'][k]) setattr(aperture, 'Idl2SciY{:d}{:d}'.format(i, j), idl2sci_factor * pcf_data[pcf_name]['D'][k]) k += 1 aperture.Idl2SciX00 = aperture.Idl2SciX00 - idl2sci_factor * aperture.XDetRef aperture.Idl2SciY00 = aperture.Idl2SciY00 - idl2sci_factor * aperture.YDetRef # get offsets from first coefficients Xgwa = aperture.Sci2IdlX00 Ygwa = aperture.Sci2IdlY00 # see Calc worksheet row 30 Xgwa_mod = -Xgwa Ygwa_mod = -Ygwa # apply polynomial transform to XAN,YAN XAN = polynomial.poly(pcf_data['CLEAR_GWA_OTE']['A'], Xgwa_mod, Ygwa_mod, order=polynomial_degree) YAN = polynomial.poly(pcf_data['CLEAR_GWA_OTE']['B'], Xgwa_mod, Ygwa_mod, order=polynomial_degree) # convert from XAN,YAN to V2,V3 and from degree to arcsecond (e.g. Cell F32) aperture.V2Ref = +1 * 3600. * XAN aperture.V3Ref = -1 * 3600. * (YAN + V3_TO_YAN_OFFSET_DEG) if verbose: print('Xgwa, Ygwa:', Xgwa, Ygwa) print('Xgwa_mod, Ygwa_mod:', Xgwa_mod, Ygwa_mod) print('XAN, YAN:', XAN, YAN) print('aperture.V2Ref, aperture.V3Ref:', aperture.V2Ref, aperture.V3Ref) # derivatives dXAN_dXgwa = polynomial.shift_coefficients(pcf_data['CLEAR_GWA_OTE']['A'], Xgwa_mod, Ygwa_mod, verbose=False)[1] dXAN_dYgwa = polynomial.shift_coefficients(pcf_data['CLEAR_GWA_OTE']['A'], Xgwa_mod, Ygwa_mod, verbose=False)[2] dYAN_dXgwa = polynomial.shift_coefficients(pcf_data['CLEAR_GWA_OTE']['B'], Xgwa_mod, Ygwa_mod, verbose=False)[1] dYAN_dYgwa = polynomial.shift_coefficients(pcf_data['CLEAR_GWA_OTE']['B'], Xgwa_mod, Ygwa_mod, verbose=False)[2] if verbose: print('dXAN_dXgwa, dXAN_dYgwa:', dXAN_dXgwa, dXAN_dYgwa) print('dYAN_dXgwa, dYAN_dYgwa:', dYAN_dXgwa, dYAN_dYgwa) if parent_aperture_name is None: dV2_dXSci = -3600. * (dXAN_dXgwa * aperture.Sci2IdlX10 + dXAN_dYgwa * aperture.Sci2IdlY10) dV2_dYSci = -3600. * (dXAN_dXgwa * aperture.Sci2IdlX11 + dXAN_dYgwa * aperture.Sci2IdlY11) dV3_dXSci = 3600. * (dYAN_dXgwa * aperture.Sci2IdlX10 + dYAN_dYgwa * aperture.Sci2IdlY10) dV3_dYSci = 3600. * (dYAN_dXgwa * aperture.Sci2IdlX11 + dYAN_dYgwa * aperture.Sci2IdlY11) else: dV2_dXSci = -3600. * (dXAN_dXgwa * parent_aperture.Sci2IdlX10 + dXAN_dYgwa * parent_aperture.Sci2IdlY10) dV2_dYSci = -3600. * (dXAN_dXgwa * parent_aperture.Sci2IdlX11 + dXAN_dYgwa * parent_aperture.Sci2IdlY11) dV3_dXSci = 3600. * (dYAN_dXgwa * parent_aperture.Sci2IdlX10 + dYAN_dYgwa * parent_aperture.Sci2IdlY10) dV3_dYSci = 3600. * (dYAN_dXgwa * parent_aperture.Sci2IdlX11 + dYAN_dYgwa * parent_aperture.Sci2IdlY11) # approximate scale terms aperture.XSciScale = np.sqrt(dV2_dXSci**2 + dV3_dXSci**2) aperture.YSciScale = np.sqrt(dV2_dYSci**2 + dV3_dYSci**2) # compute the approximate angles betaY = np.rad2deg(np.arctan2(dV2_dYSci, dV3_dYSci)) betaX = np.rad2deg(np.arctan2(dV2_dXSci, dV3_dXSci)) if verbose: print('dV2_dXSci, dV2_dYSci, dV3_dXSci, dV3_dYSci:', dV2_dXSci, dV2_dYSci, dV3_dXSci, dV3_dYSci) print('betaY:', betaY) # set the aperture attributes aperture.V3SciXAngle = betaX aperture.V3SciYAngle = betaY aperture.V3IdlYAngle = aperture.V3SciYAngle # The usual SIAF ideal plane is completely bypassed in the target acquisition calculations. # In the OSS and FULLSCA rows, an ideal plane is nevertheless defined by choosing a reference # point near the center of each detector and using the combined TA transformations to project # the detector reference points and corners onto the sky. # Compute aperture corners in different frames: Calc worksheep row 43 sci_corners_x, sci_corners_y = aperture.corners('sci', rederive=True) # offset from reference location sci_corners_x -= aperture.XSciRef sci_corners_y -= aperture.YSciRef # compute GWA plane corners # These coefficients are name overloaded and implement the transformation to GWA plane gwa_coefficients_x = np.array([ getattr(aperture, s) for s in DISTORTION_ATTRIBUTES if 'Sci2IdlX' in s ]) gwa_coefficients_y = np.array([ getattr(aperture, s) for s in DISTORTION_ATTRIBUTES if 'Sci2IdlY' in s ]) gwa_corners_x = np.zeros(len(sci_corners_x)) gwa_corners_y = np.zeros(len(sci_corners_y)) # apply transformation to GWA plane for j in range(len(gwa_corners_x)): gwa_corners_x[j] = polynomial.poly(gwa_coefficients_x, sci_corners_x[j], sci_corners_y[j], order=aperture.Sci2IdlDeg) gwa_corners_y[j] = polynomial.poly(gwa_coefficients_y, sci_corners_x[j], sci_corners_y[j], order=aperture.Sci2IdlDeg) # compute corners in V2V3/Tel gwa_to_ote_coefficients_x = pcf_data['CLEAR_GWA_OTE']['A'] gwa_to_ote_coefficients_y = pcf_data['CLEAR_GWA_OTE']['B'] tel_corners_x = np.zeros(len(sci_corners_x)) tel_corners_y = np.zeros(len(sci_corners_y)) for j in range(len(gwa_corners_x)): tel_corners_x[j] = +3600 * polynomial.poly(gwa_to_ote_coefficients_x, -gwa_corners_x[j], -gwa_corners_y[j], order=aperture.Sci2IdlDeg) tel_corners_y[j] = -3600 * ( polynomial.poly(gwa_to_ote_coefficients_y, -gwa_corners_x[j], -gwa_corners_y[j], order=aperture.Sci2IdlDeg) + V3_TO_YAN_OFFSET_DEG) # Ideal corners idl_corners_x = np.zeros(len(sci_corners_x)) idl_corners_y = np.zeros(len(sci_corners_y)) for j in range(len(gwa_corners_x)): idl_corners_x[j] = aperture.VIdlParity * ( tel_corners_x[j] - aperture.V2Ref) * np.cos( np.deg2rad(aperture.V3IdlYAngle)) - aperture.VIdlParity * ( tel_corners_y[j] - aperture.V3Ref) * np.sin( np.deg2rad(aperture.V3IdlYAngle)) idl_corners_y[j] = (tel_corners_x[j] - aperture.V2Ref) * np.sin( np.deg2rad(aperture.V3IdlYAngle)) + ( tel_corners_y[j] - aperture.V3Ref) * np.cos( np.deg2rad(aperture.V3IdlYAngle)) setattr(aperture, 'XIdlVert{}'.format(j + 1), idl_corners_x[j]) setattr(aperture, 'YIdlVert{}'.format(j + 1), idl_corners_y[j]) if verbose: print('sci_corners_x, sci_corners_y:', sci_corners_x, sci_corners_y) print(gwa_corners_x, gwa_corners_y) print(tel_corners_x, tel_corners_y) print(idl_corners_x, idl_corners_y) return aperture