//  [python]
def kernel(variable, ell, nu, eig, fgong):
    """Returns a structural kernel.  I have tried to make this as
    notationally similar to Gough & Thompson (1991) as possible.

    Parameters
    ----------
    variable: str
        Either 'rho' or 'c' to select whether a density or sound speed
        kernel is returned.
    ell: int
        The angular degree of the mode.
    nu: float
        The cyclic frequency of the mode.
    eig: np.array, shape(N,7)
        Eigenfrequency data for the mode, as produced by ADIPLS.
    fgong: dict
        Stellar model data in a dictionary, as per load_fgong()
        above.

    Returns
    -------
    kernel: np.array, length N
        The density or sound speed structure kernel.
    """

    def integrate(y, x):
        return np.hstack((0., cumulative_trapezoid(y, x)))

    def complement(y, x):
        return np.trapz(y, x) - integrate(y, x)

    omega = 2.*np.pi*nu                              # convert to cyclic frequency
    G = 6.672e-8                                     # gravitational constant
    L2 = ell*(ell+1)
    L = np.sqrt(L2)
    M, R = fgong['glob'][:2]                         # mass and radius from FGONG
    sigma = np.sqrt(R**3/G/M)*omega                  # dimensionless frequency

    r = fgong['var'][::-1,0]                         # radial co-ordinate
    m = M*np.exp(fgong['var'][::-1,1])               # mass co-ordinate
    P = fgong['var'][::-1,3]                         # pressure
    rho = fgong['var'][::-1,4]                       # density
    Gamma1 = fgong['var'][::-1,9]                    # first adiabatic index
    cs2 = Gamma1*P/rho                               # square of the sound speed
    A1 = (m/M)/(r/R)**3
    A2 = (G*m*rho)/(Gamma1*P*r)
    Vg = A2[:]
    drho_dr = -(Vg+fgong['var'][::-1,14])*rho/r      # density gradient

    x = eig[:,0]                                     # dimensionless radius (i.e. r/R)
    i0 = np.argmin((x-1.)**2)                        # do I actually use this!?
    xi_r = eig[:,1]*R                                # radial component of eigenfunction

    dxi_r_dr = np.hstack((0., np.diff(xi_r)/np.diff(r)))

    # chi is the "dilatation"
    if ell == 0:
        xi_h = 0.*xi_r # radial modes have zero horizontal component
        eta = G*m/r**3/omega**2
        chi = Vg/x*(eig[:,1]-sigma**2/A1/x*eig[:,2])
    elif ell > 0:
        xi_h = eig[:,2]*R/L2
        eta = L2*G*m/r**3/omega**2
        chi = Vg/x*(eig[:,1]-sigma**2/A1/L2*eig[:,2]-eig[:,3])
    else:
        raise ValueError('ell must be non-negative')

    # most stellar models include the central point, at which the
    # numerical value of chi and drho_dr might be buggy.
    chi[0] = 0.
    drho_dr[0] = 0.

    S = np.trapz((xi_r**2+L2*xi_h**2)*rho*r**2, r)

    if variable == 'c':
        K = rho*cs2*chi**2*r**2 # c.f. equation (60)
    elif variable == 'rho':
        # first compute the huge bracketed terms in last two lines of equation (61)
        K = (ell+1.)/r**ell*(xi_r-ell*xi_h)*integrate((rho*chi+xi_r*drho_dr)*r**(ell+2.), r) \
            - ell*r**(ell+1.)*(xi_r+(ell+1.)*xi_h)*complement((rho*chi+xi_r*drho_dr)*r**(1.-ell), r)

        # then combine it with the rest
        K = -0.5*(xi_r**2+L2*xi_h**2)*rho*omega**2*r**2 \
            +0.5*rho*cs2*chi**2*r**2 \
            -G*m*(chi*rho+0.5*xi_r*drho_dr)*xi_r \
            -4*np.pi*G*rho*r**2*complement((chi*rho+0.5*xi_r*drho_dr)*xi_r, r) \
            +G*m*rho*xi_r*dxi_r_dr \
            +0.5*G*(m*drho_dr+4.*np.pi*r**2*rho**2)*xi_r**2 \
            -4*np.pi*G*rho/(2.*ell+1.)*K
    else:
        raise ValueError('variable can only be c or rho')

    return K/(S*omega**2)