コード例 #1
0
ファイル: generate_miri.py プロジェクト: charlesrp/pysiaf
def invcheck(A, B, C, D, order, low, high):
    """Round trip calculation to test inversion.

    Parameters
    ----------
    A
    B
    C
    D
    order
    low
    high

    """
    x = np.random.random(10)
    x = low + (high - low) * x
    y = np.random.random(10)
    y = low + (high - low) * y
    u = polynomial.poly(A, x, y, order)
    v = polynomial.poly(B, x, y, order)
    x2 = polynomial.poly(C, u, v, order)
    y2 = polynomial.poly(D, u, v, order)
    print('\n INVERSE CHECK')
    for i in range(10):
        print(
            '%10.4f%10.4f%10.4f%10.4f%10.4f%10.4f%10.2e%10.2e' %
            (x[i], y[i], u[i], v[i], x2[i], y2[i], x2[i] - x[i], y2[i] - y[i]))

    print('Round trip errors %10.2e %10.2e' % ((x - x2).std(), (y - y2).std()))
    print('Round trip errors %10.3f %10.3f' % ((x - x2).std(), (y - y2).std()))
コード例 #2
0
def test_RotateCoeffs(verbose=False):
    """Test accuracy of inversion method"""

    # First invent a random but plausible polynomial array
    a = makeup_polynomial()
    order = 5
    if verbose:
        print('A')
        polynomial.print_triangle(a)

    # Random point within 2048 square with origin at the center
    np.random.seed(seed=1)
    [x, y] = 2048.0 * np.random.rand(2) - 1024.0
    u = polynomial.poly(a, x, y, order)

    # Random angle

    theta = 360 * np.random.rand(1)
    if verbose:
        print('Angle', theta)
    thetar = np.radians(theta)
    xp = x * np.cos(thetar) - y * np.sin(thetar)
    yp = x * np.sin(thetar) + y * np.cos(thetar)
    ap = polynomial.prepend_rotation_to_polynomial(a, theta, order)
    u = polynomial.poly(a, x, y, order)
    up = polynomial.poly(ap, xp, yp,
                         order)  # using transformed point and polynomial
    if verbose:
        print('X Y', x, y)
        print('U', u)
        print('XP YP', xp, yp)
        print('UP', up)
        print('UP-U', up - u)
    assert abs(up - u) < 1.0e-12, 'Inaccurate transformation conversion'
コード例 #3
0
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
コード例 #4
0
ファイル: test_polynomial.py プロジェクト: ColinRCox/pysiaf
def test_invert(verbose=True):
    """Test accuracy of inversion method"""

    # First invent a plausible pair of polynomial arrays
    order = 5
    a = makeup_polynomial()
    b = makeup_polynomial()
    # Modify linear terms in b array so sclae term is b[2]
    btemp = b[1]
    b[1] = b[2]
    b[2] = btemp

    if verbose:
        print('A')
        polynomial.triangle(a, 5)
        print('B')
        polynomial.triangle(b, 5)

    # Random point within 2048 square with origin at the center
    np.random.seed(seed=1)
    (x, y) = 2048.0 * np.random.rand(2) - 1024.0
    u = polynomial.poly(a, x, y, order)
    v = polynomial.poly(b, x, y, order)
    if verbose:
        print('X Y', x, y)
        print('U V', u, v)

    (x2, y2, error, iterations) = polynomial.invert(a,
                                                    b,
                                                    u,
                                                    v,
                                                    order,
                                                    verbose=verbose)

    if verbose:
        print('Error', error, ' after', iterations, ' iterations')
        print('X2 Y2', x2, y2)
        print('Dx Dy', x2 - x, y2 - y)
    assert abs(x2 - x) < 1.0e-12 and abs(y2 - y) < 1.0e-12, 'Error too large'

    return
コード例 #5
0
def test_two_step(verbose=False):
    # make up random polynomials of order 5 with terms which decrease strongly with power.
    A = makeup_polynomial()
    B = makeup_polynomial()
    a = np.array([1.0, 0.5, 0.1])
    b = np.array([2.0, 0.2, 0.6])

    (A2, B2) = polynomial.two_step(A, B, a, b)
    if verbose:
        print('\nA')
        polynomial.print_triangle(A)  #     print('B')
        print('B')
        polynomial.print_triangle(B)
        print('\nLinear terms')
        print('a', a)
        print('b', b)
        print('\nA2')
        polynomial.print_triangle(A2)
        print('B2')
        polynomial.print_triangle(B2)

    # Now do a test calculation
    (x, y) = (10, 5)
    xp = a[0] + a[1] * x + a[2] * y
    yp = b[0] + b[1] * x + b[2] * y
    u = polynomial.poly(A, xp, yp, 5)
    v = polynomial.poly(B, xp, yp, 5)
    up = polynomial.poly(A2, x, y, 5)
    vp = polynomial.poly(B2, x, y, 5)

    if verbose:
        print('x,y', x, y)
        print('xp,yp', xp, yp)
        print('Two step', u, v)
        print('One step', up, vp)

    assert abs(u - up) < 1.0e-12 and abs(
        v - vp) < 1.0e-12, 'Inaccurate transformation'
コード例 #6
0
def test_poly(verbose=False):
    """ Tests polynomial evaluation by calculating a 9 by 9 array across a 2048 pixel grid
    Then polyfit is used to fit the calculated points to generate a new pair of polynomials
    Finally the original x,y points are used in the new polynomials and the outputs compared
    This incidentally provides a robust test of polyfit

    parameters
    verbose: logical value. If True, print statements and graph will be output
            if False or unassigned there will be no output unless the assert statement issues
            a Polynomial inaccuracy message.
    return: None """

    [x, y] = np.mgrid[0:9, 0:9]
    xp = 256.0 * x - 1024.0
    yp = 256.0 * y - 1024.0
    #u = np.zeros((9, 9))
    #v = np.zeros((9, 9))

    # Random polynomials
    a = makeup_polynomial()
    b = makeup_polynomial()
    # Switch linear terms for b coefficients so that b[2] is approximate scale
    btemp = b[1]
    b[1] = b[2]
    b[2] = btemp

    if verbose:
        print('A coefficients')
        polynomial.print_triangle(a)
        print('B coefficients')
        polynomial.print_triangle(b)

    # Evaluate polynomials acting on x,y arrays
    u = polynomial.poly(a, x, y, 5)
    v = polynomial.poly(b, x, y, 5)
    # Fit new polynomials to calculated positions
    s1 = polynomial.polyfit(u, x, y, 5)
    s2 = polynomial.polyfit(v, x, y, 5)
    # Evaluate new polynomials
    uc = polynomial.poly(s1, x, y, 5)
    vc = polynomial.poly(s2, x, y, 5)
    # Compare outputs
    du = uc - u
    dv = vc - v
    u_std = np.std(du)
    v_std = np.std(dv)

    if verbose:
        print('Fitted polynomials')
        print('S1')
        polynomial.print_triangle(s1)
        print('S2')
        polynomial.print_triangle(s2)
        print('Fit comparison STDs {:10.2e} {:10.2e}'.format(u_std, v_std))
        pl.figure(1)
        pl.clf()
        pl.grid(True)
        pl.plot(u, v, 'gx')
        pl.plot(uc, vc, 'r+')

    assert u_std < 1.0e-12 and v_std < 1.0e-12, 'Polynomial inaccuracy'
    return None
コード例 #7
0
ファイル: generate_miri.py プロジェクト: charlesrp/pysiaf
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
コード例 #8
0
ファイル: generate_miri.py プロジェクト: charlesrp/pysiaf
        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][
            'C_shifted'] = polynomial.shift_coefficients(
                csv_data[csv_aperture_name]['C'], dxIdl, dyIdl, verbose=False)
        csv_data[csv_aperture_name][
            'D_shifted'] = polynomial.shift_coefficients(
                csv_data[csv_aperture_name]['D'], dxIdl, dyIdl, verbose=False)

        # set 00 coefficients to zero
        for coefficient_name in [
コード例 #9
0
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.ShiftCoeffs(
            pcf_data[pcf_name]['{}'.format(axis)], Xref, Yref, order=polynomial_degree,
            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)], order=polynomial_degree)

    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.ShiftCoeffs(pcf_data[pcf_name]['{}_modified'.format('A')],
                                                   Xoffset, Yoffset, order=polynomial_degree,
                                                   verbose=False)
    sci2idly_coefficients = polynomial.ShiftCoeffs(pcf_data[pcf_name]['{}_modified'.format('B')],
                                                   Xoffset, Yoffset, order=polynomial_degree,
                                                   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.ShiftCoeffs(pcf_data['CLEAR_GWA_OTE']['A'], Xgwa_mod, Ygwa_mod,
                                        order=polynomial_degree, verbose=False)[1]
    dXAN_dYgwa = polynomial.ShiftCoeffs(pcf_data['CLEAR_GWA_OTE']['A'], Xgwa_mod, Ygwa_mod,
                                        order=polynomial_degree, verbose=False)[2]
    dYAN_dXgwa = polynomial.ShiftCoeffs(pcf_data['CLEAR_GWA_OTE']['B'], Xgwa_mod, Ygwa_mod,
                                        order=polynomial_degree, verbose=False)[1]
    dYAN_dYgwa = polynomial.ShiftCoeffs(pcf_data['CLEAR_GWA_OTE']['B'], Xgwa_mod, Ygwa_mod,
                                        order=polynomial_degree, 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