예제 #1
0
    def residual(tau_ratio):
        tau_ratio = tau_ratio[0]
        # mixture quantitities
        f12, f21, u12, T12 = distributions.equilibrium_maxwellian3D(
                n1, n2, u1, u2, T1, T2, m1, m2, vx1, vy1, vz1, vx2, vy2, vz2,
                tau_ratio, return_all=True)

        # dH in the kinetic sense
        dH12_bgk = (triple_integral(f12 * np.log(f1), vx1, vy1, vz1) - 
                    triple_integral(f1 * np.log(f1), vx1, vy1, vz1))
        dH21_bgk = (triple_integral(f21 * np.log(f2), vx2, vy2, vz2) - 
                    triple_integral(f2 * np.log(f2), vx2, vy2, vz2))
        tau_kl = ((dHdt12 * dH12_bgk + tau_ratio * dHdt21 * dH21_bgk) / 
                  (dHdt12**2 + dHdt21**2 * tau_ratio**2))
        resid =  np.array([(dHdt12 - dH12_bgk / tau_kl) / dHdt12,
                           (dHdt21 - dH21_bgk / (tau_kl * tau_ratio)) / dHdt21])
        logging.debug('residual: ' + np.array_str(resid))
        logging.debug('ratio of taus: %f' % tau_ratio)
        return resid
예제 #2
0
def compute_interspecies_tau(distribution1, distribution2, dHdt12, dHdt21):
    ''' compute the interspecies relaxation parameters by a nonlinear least
    squares solve to find the optimal ratio of taus
    

    Parameters
    ----------
    distribution1, distribution2 : distribution objects
        the distributions of species 1 and 2
    dHdt12, dHdt21 : floats
        dHdt of species 1 due to species 2 and vice-versa, respectively

    Returns
    -------
    tau12, tau21 : floats
        relaxation time of species 1 due to species 2 and vice-versa
    error : array to two floats
        error from the taus
    f12, f21 : distributions
        interspecies equilibrium distributions being relaxed towards

    @TODO Fix analytical jacobian at some point
    '''

    # setup (extract data from distributions)
    vx1 = distribution1._x
    vx2 = distribution2._x
    vy1 = distribution1._y
    vy2 = distribution2._y
    vz1 = distribution1._z
    vz2 = distribution2._z
    f1 = distribution1.distribution
    f2 = distribution2.distribution
    m1 = distribution1.mass
    m2 = distribution2.mass
    n1 = distribution1.density
    n2 = distribution2.density
    u1 = distribution1.momentum / m1
    u2 = distribution2.momentum / m2
    T1 = distribution1.kinetic_energy * 2. / 3.
    T2 = distribution2.kinetic_energy * 2. / 3.

    # initial guess for tau ratio, assume temperature relaxation
    tau_ratio0 = n2 / n1
    tau_ratio = tau_ratio0

    #   # mixture quantitities
    #   f12, f21, u12, T12 = distributions.equilibrium_maxwellian3D(
    #           n1, n2, u1, u2, T1, T2, m1, m2, vx1, vy1, vz1, vx2, vy2, vz2,
    #           tau_ratio0, return_all=True)

    #   # dH in the kinetic sense
    #   dH12_bgk = (triple_integral(f12 * np.log(f1), vx1, vy1, vz1) -
    #               triple_integral(f1 * np.log(f1), vx1, vy1, vz1))
    #   dH21_bgk = (triple_integral(f21 * np.log(f2), vx2, vy2, vz2) -
    #               triple_integral(f2 * np.log(f2), vx2, vy2, vz2))

    #   # least squares functions
    #   def residual(tau_ratio):
    #       tau_ratio = tau_ratio[0]
    #       # mixture quantitities
    #       f12, f21, u12, T12 = distributions.equilibrium_maxwellian3D(
    #               n1, n2, u1, u2, T1, T2, m1, m2, vx1, vy1, vz1, vx2, vy2, vz2,
    #               tau_ratio, return_all=True)

    #       # dH in the kinetic sense
    #       dH12_bgk = (triple_integral(f12 * np.log(f1), vx1, vy1, vz1) -
    #                   triple_integral(f1 * np.log(f1), vx1, vy1, vz1))
    #       dH21_bgk = (triple_integral(f21 * np.log(f2), vx2, vy2, vz2) -
    #                   triple_integral(f2 * np.log(f2), vx2, vy2, vz2))
    #       tau_kl = ((dHdt12 * dH12_bgk + tau_ratio * dHdt21 * dH21_bgk) /
    #                 (dHdt12**2 + dHdt21**2))
    #       tau_kl = (((dH12_bgk * dHdt21 * tau_ratio)**2 + (dH21_bgk * dHdt12)**2) /
    #                 (dHdt12 * dHdt21 * tau_ratio *
    #                  (dH12_bgk * dHdt21 * tau_ratio + dH21_bgk * dHdt12)))
    #       resid =  np.array([(dHdt12 - dH12_bgk / tau_kl) / dHdt12,
    #                          (dHdt21 - dH21_bgk / (tau_kl * tau_ratio)) / dHdt21])
    #       logging.debug('residual: ' + np.array_str(resid))
    #       logging.debug('ratio of taus: %f' % tau_ratio)
    #       return resid

    #   def jacobian(tau_ratio):
    #       ''' ANALYTICAL JACOBIAN DOES NOT CURRENTLY SEEM TO WORK '''
    #       tau_ratio = tau_ratio[0]

    #       # mixture quantitities
    #       f12, f21, u12, T12 = distributions.equilibrium_maxwellian3D(
    #               n1, n2, u1, u2, T1, T2, m1, m2, vx1, vy1, vz1, vx2, vy2, vz2,
    #               tau_ratio, return_all=True)
    #       A12 = n1 * (m1 / (2. * np.pi * T12))**(3./2.)
    #       A21 = n2 * (m2 / (2. * np.pi * T12))**(3./2.)
    #       # dH in the kinetic sense
    #       dH12_bgk = (triple_integral(f12 * np.log(f1), vx1, vy1, vz1) -
    #                   triple_integral(f1 * np.log(f1), vx1, vy1, vz1))
    #       dH21_bgk = (triple_integral(f21 * np.log(f2), vx2, vy2, vz2) -
    #                   triple_integral(f2 * np.log(f2), vx2, vy2, vz2))
    #       tau12 = (((dH12_bgk * dHdt21 * tau_ratio)**2 + (dH21_bgk * dHdt12)**2) /
    #                (dHdt12 * dHdt21 * tau_ratio *
    #                 (dH12_bgk * dHdt21 * tau_ratio + dH21_bgk * dHdt12)))

    #       # integrals
    #       vsq1 = (vx1 - u12[0])**2 + (vy1 - u12[1])**2 + (vz1 - u12[2])**2
    #       f1logf1 = triple_integral(f1 * np.log(f1), vx1, vy1, vz1)
    #       f12logf1 = triple_integral(f12 * np.log(f1), vx1, vy1, vz1)
    #       vf12logf1 = np.array([
    #           triple_integral((vx1 - u12[0]) * f12 * np.log(f1), vx1, vy1, vz1),
    #           triple_integral((vy1 - u12[1]) * f12 * np.log(f1), vx1, vy1, vz1),
    #           triple_integral((vz1 - u12[2]) * f12 * np.log(f1), vx1, vy1, vz1)])
    #       vsqf12logf1 = triple_integral(vsq1 * f12 * np.log(f1), vx1, vy1, vz1)
    #       vsq2 = (vx2 - u12[0])**2 + (vy2 - u12[1])**2 + (vz2 - u12[2])**2
    #       f2logf2 = triple_integral(f2 * np.log(f2), vx2, vy2, vz2)
    #       f21logf2 = triple_integral(f21 * np.log(f2), vx2, vy2, vz2)
    #       vf21logf2 = np.array([
    #           triple_integral((vx2 - u12[0]) * f21 * np.log(f2), vx2, vy2, vz2),
    #           triple_integral((vy2 - u12[1]) * f21 * np.log(f2), vx2, vy2, vz2),
    #           triple_integral((vz2 - u12[2]) * f21 * np.log(f2), vx2, vy2, vz2)])
    #       vsqf21logf2 = triple_integral(vsq2 * f21 * np.log(f2), vx2, vy2, vz2)
    #

    #       # derivatives
    #       du12 = ((n1 * n2 * m1 * m2) / (n1 * m1 * tau_ratio + n2 * m2)**2 *
    #               (u1 - u2))
    #       dT12 = (1. / (3. * tau_ratio * n1 + 3. * n2)**2 *
    #               (9. * n1 * n2 * (T1 - T2) +
    #                3. * n1 * n2 * (m1 * np.sum(u1**2 - u12**2) -
    #                                m2 * np.sum(u2**2 - u12**2))) -
    #               (2. * m1 * m2 * n1 * n2 * (tau_ratio * m1 * n1 *
    #                np.sum(u1**2 - u1 * u2) + m2 * n2 * np.sum(u1 * u2 - u2**2))) /
    #               ((3. * tau_ratio * n1 + 3. * n2) * (tau_ratio * m1 * n1 +
    #                m2 * n2)**2))
    #       dA12 = (-3. * n1 / 2. * (m1 / (2. * np.pi))**(3./2.) *
    #               T12**(-5./2.) * dT12)
    #       dA21 = (-3. * n2 / 2. * (m2 / (2. * np.pi))**(3./2.) *
    #               T12**(-5./2.) * dT12)
    #       dtau12 = (dH21_bgk * ((dH12_bgk * dHdt21 * tau_ratio)**2 -
    #                  2 * tau_ratio * dH12_bgk * dH21_bgk * dHdt12 * dHdt21 -
    #                  (dH21_bgk * dHdt12)**2) /
    #                 (dHdt21 * tau_ratio**2 * ((dH12_bgk * dHdt21 * tau_ratio)**2 +
    #                  2 * tau_ratio * dH12_bgk * dH21_bgk * dHdt12 * dHdt21 +
    #                  (dH21_bgk * dHdt12)**2)))
    #       dH12 = (f12logf1 * dA12 / A12 +
    #                   np.sum(vf12logf1 * m1 / T12 * du12) +
    #                   vsqf12logf1 * m1 / (2. * T12**2) * dT12)
    #       dH21 = (f21logf2 * dA21 / A21 +
    #                   np.sum(vf21logf2 * m2 / T12 * du12) +
    #                   vsqf21logf2 * m2 / (2. * T12**2) * dT12)
    #       dH12dt_bgk = f12logf1 - f1logf1
    #       dH21dt_bgk = f21logf2 - f2logf2
    #       dG1 = (1. / tau12**2 * dH12dt_bgk / dHdt12 * dtau12 -
    #              1. / (tau12 * dHdt12) * dH12)
    #       dG2 = ((1. / (tau_ratio**2 * tau12) +
    #               1. / (tau_ratio * tau12**2) * dtau12) * dH21dt_bgk / dHdt21 -
    #              1. / (tau_ratio * tau12 * dHdt21) * dH21)
    #       dG = np.array([dG1, dG2]).reshape((2,1))
    #       return dG
    #
    #   logging.debug('doing least squares optimization')
    #   temperature_tau_ratio = n2 / n1
    #   momentum_tau_ratio = (m2 * n2) / (m1 * n1)
    #   lb = min(temperature_tau_ratio, momentum_tau_ratio)
    #   ub = max(temperature_tau_ratio, momentum_tau_ratio)
    #   output = scipy.optimize.least_squares(residual, tau_ratio0, #jac=jacobian,
    #                                         bounds=(lb, ub),
    #                                         ftol=1e-6, xtol=1e-6, gtol=1e-6)
    #   tau_ratio = output.x[0]

    # mixture quantitities
    f12, f21, u12, T12 = distributions.equilibrium_maxwellian3D(
        n1,
        n2,
        u1,
        u2,
        T1,
        T2,
        m1,
        m2,
        vx1,
        vy1,
        vz1,
        vx2,
        vy2,
        vz2,
        tau_ratio,
        return_all=True)

    # dH in the kinetic sense
    dH12_bgk = (triple_integral(f12 * np.log(f1), vx1, vy1, vz1) -
                triple_integral(f1 * np.log(f1), vx1, vy1, vz1))
    dH21_bgk = (triple_integral(f21 * np.log(f2), vx2, vy2, vz2) -
                triple_integral(f2 * np.log(f2), vx2, vy2, vz2))
    tau12 = ((dHdt12 * dH12_bgk + tau_ratio * dHdt21 * dH21_bgk) /
             (dHdt12**2 + dHdt21**2))
    tau21 = tau_ratio * tau12
    error = [-1.0, -1.0]  #abs(output.fun)# / np.array([dHdt12, dHdt21]))
    #   logging.debug('least squares terminated due to: ' + output.message)

    return tau12, tau21, error, f12, f21
예제 #3
0
def compute_interspecies_tau(distribution1, distribution2, dHdt12, dHdt21):
    ''' compute the interspecies relaxation parameters by a nonlinear least
    squares solve to find the optimal ratio of taus
    

    Parameters
    ----------
    distribution1, distribution2 : distribution objects
        the distributions of species 1 and 2
    dHdt12, dHdt21 : floats
        dHdt of species 1 due to species 2 and vice-versa, respectively

    Returns
    -------
    tau12, tau21 : floats
        relaxation time of species 1 due to species 2 and vice-versa

    @TODO Fix analytical jacobian at some point
    '''

    # setup (extract data from distributions)
    vx1 = distribution1._x
    vx2 = distribution2._x
    vy1 = distribution1._y
    vy2 = distribution2._y
    vz1 = distribution1._z
    vz2 = distribution2._z
    f1 = distribution1.distribution
    f2 = distribution2.distribution
    m1 = distribution1.mass
    m2 = distribution2.mass
    n1 = distribution1.density
    n2 = distribution2.density
    u1 = distribution1.momentum / m1
    u2 = distribution2.momentum / m2
    T1 = distribution1.kinetic_energy * 2. / 3.
    T2 = distribution2.kinetic_energy * 2. / 3.

    # initial guess for tau ratio, assume temperature relaxation
    tau_ratio0 = n2 / n1

    # mixture quantitities
    f12, f21, u12, T12 = distributions.equilibrium_maxwellian3D(
        n1,
        n2,
        u1,
        u2,
        T1,
        T2,
        m1,
        m2,
        vx1,
        vy1,
        vz1,
        vx2,
        vy2,
        vz2,
        tau_ratio0,
        return_all=True)

    # dH in the kinetic sense
    dH12_bgk = (triple_integral(f12 * np.log(f1), vx1, vy1, vz1) -
                triple_integral(f1 * np.log(f1), vx1, vy1, vz1))
    dH21_bgk = (triple_integral(f21 * np.log(f2), vx2, vy2, vz2) -
                triple_integral(f2 * np.log(f2), vx2, vy2, vz2))

    # least squares functions
    def residual(tau_ratio):
        tau_ratio = tau_ratio[0]
        # mixture quantitities
        f12, f21, u12, T12 = distributions.equilibrium_maxwellian3D(
            n1,
            n2,
            u1,
            u2,
            T1,
            T2,
            m1,
            m2,
            vx1,
            vy1,
            vz1,
            vx2,
            vy2,
            vz2,
            tau_ratio,
            return_all=True)

        # dH in the kinetic sense
        dH12_bgk = (triple_integral(f12 * np.log(f1), vx1, vy1, vz1) -
                    triple_integral(f1 * np.log(f1), vx1, vy1, vz1))
        dH21_bgk = (triple_integral(f21 * np.log(f2), vx2, vy2, vz2) -
                    triple_integral(f2 * np.log(f2), vx2, vy2, vz2))
        #       tau_kl = ((dHdt12 * dH12_bgk + tau_ratio * dHdt21 * dH21_bgk) /
        #                 (dHdt12**2 + dHdt21**2))
        tau_kl = (((dH12_bgk * dHdt21 * tau_ratio)**2 +
                   (dH21_bgk * dHdt12)**2) /
                  (dHdt12 * dHdt21 * tau_ratio *
                   (dH12_bgk * dHdt21 * tau_ratio + dH21_bgk * dHdt12)))
        resid = np.array([(dHdt12 - dH12_bgk / tau_kl) / dHdt12,
                          (dHdt21 - dH21_bgk / (tau_kl * tau_ratio)) / dHdt21])
        logging.debug('residual: ' + np.array_str(resid))
        logging.debug('ratio of taus: %d' % tau_ratio)
        return resid

    def jacobian(tau_ratio):
        ''' ANALYTICAL JACOBIAN DOES NOT CURRENTLY SEEM TO WORK '''
        tau_ratio = tau_ratio[0]

        # mixture quantitities
        f12, f21, u12, T12 = distributions.equilibrium_maxwellian3D(
            n1,
            n2,
            u1,
            u2,
            T1,
            T2,
            m1,
            m2,
            vx1,
            vy1,
            vz1,
            vx2,
            vy2,
            vz2,
            tau_ratio,
            return_all=True)
        A12 = n1 * (m1 / (2. * np.pi * T12))**(3. / 2.)
        A21 = n2 * (m2 / (2. * np.pi * T12))**(3. / 2.)
        # dH in the kinetic sense
        dH12_bgk = (triple_integral(f12 * np.log(f1), vx1, vy1, vz1) -
                    triple_integral(f1 * np.log(f1), vx1, vy1, vz1))
        dH21_bgk = (triple_integral(f21 * np.log(f2), vx2, vy2, vz2) -
                    triple_integral(f2 * np.log(f2), vx2, vy2, vz2))
        tau12 = (((dH12_bgk * dHdt21 * tau_ratio)**2 +
                  (dH21_bgk * dHdt12)**2) /
                 (dHdt12 * dHdt21 * tau_ratio *
                  (dH12_bgk * dHdt21 * tau_ratio + dH21_bgk * dHdt12)))

        # integrals
        vsq1 = (vx1 - u12[0])**2 + (vy1 - u12[1])**2 + (vz1 - u12[2])**2
        f1logf1 = triple_integral(f1 * np.log(f1), vx1, vy1, vz1)
        f12logf1 = triple_integral(f12 * np.log(f1), vx1, vy1, vz1)
        vf12logf1 = np.array([
            triple_integral((vx1 - u12[0]) * f12 * np.log(f1), vx1, vy1, vz1),
            triple_integral((vy1 - u12[1]) * f12 * np.log(f1), vx1, vy1, vz1),
            triple_integral((vz1 - u12[2]) * f12 * np.log(f1), vx1, vy1, vz1)
        ])
        vsqf12logf1 = triple_integral(vsq1 * f12 * np.log(f1), vx1, vy1, vz1)
        vsq2 = (vx2 - u12[0])**2 + (vy2 - u12[1])**2 + (vz2 - u12[2])**2
        f2logf2 = triple_integral(f2 * np.log(f2), vx2, vy2, vz2)
        f21logf2 = triple_integral(f21 * np.log(f2), vx2, vy2, vz2)
        vf21logf2 = np.array([
            triple_integral((vx2 - u12[0]) * f21 * np.log(f2), vx2, vy2, vz2),
            triple_integral((vy2 - u12[1]) * f21 * np.log(f2), vx2, vy2, vz2),
            triple_integral((vz2 - u12[2]) * f21 * np.log(f2), vx2, vy2, vz2)
        ])
        vsqf21logf2 = triple_integral(vsq2 * f21 * np.log(f2), vx2, vy2, vz2)

        # derivatives
        du12 = ((n1 * n2 * m1 * m2) / (n1 * m1 * tau_ratio + n2 * m2)**2 *
                (u1 - u2))
        dT12 = (1. / (3. * tau_ratio * n1 + 3. * n2)**2 *
                (9. * n1 * n2 * (T1 - T2) + 3. * n1 * n2 *
                 (m1 * np.sum(u1**2 - u12**2) - m2 * np.sum(u2**2 - u12**2))) -
                (2. * m1 * m2 * n1 * n2 *
                 (tau_ratio * m1 * n1 * np.sum(u1**2 - u1 * u2) +
                  m2 * n2 * np.sum(u1 * u2 - u2**2))) /
                ((3. * tau_ratio * n1 + 3. * n2) *
                 (tau_ratio * m1 * n1 + m2 * n2)**2))
        dA12 = (-3. * n1 / 2. * (m1 / (2. * np.pi))**(3. / 2.) *
                T12**(-5. / 2.) * dT12)
        dA21 = (-3. * n2 / 2. * (m2 / (2. * np.pi))**(3. / 2.) *
                T12**(-5. / 2.) * dT12)
        dtau12 = (dH21_bgk *
                  ((dH12_bgk * dHdt21 * tau_ratio)**2 -
                   2 * tau_ratio * dH12_bgk * dH21_bgk * dHdt12 * dHdt21 -
                   (dH21_bgk * dHdt12)**2) /
                  (dHdt21 * tau_ratio**2 *
                   ((dH12_bgk * dHdt21 * tau_ratio)**2 +
                    2 * tau_ratio * dH12_bgk * dH21_bgk * dHdt12 * dHdt21 +
                    (dH21_bgk * dHdt12)**2)))
        dH12 = (f12logf1 * dA12 / A12 + np.sum(vf12logf1 * m1 / T12 * du12) +
                vsqf12logf1 * m1 / (2. * T12**2) * dT12)
        dH21 = (f21logf2 * dA21 / A21 + np.sum(vf21logf2 * m2 / T12 * du12) +
                vsqf21logf2 * m2 / (2. * T12**2) * dT12)
        dH12dt_bgk = f12logf1 - f1logf1
        dH21dt_bgk = f21logf2 - f2logf2
        dG1 = (1. / tau12**2 * dH12dt_bgk / dHdt12 * dtau12 - 1. /
               (tau12 * dHdt12) * dH12)
        dG2 = ((1. / (tau_ratio**2 * tau12) + 1. /
                (tau_ratio * tau12**2) * dtau12) * dH21dt_bgk / dHdt21 - 1. /
               (tau_ratio * tau12 * dHdt21) * dH21)
        dG = np.array([dG1, dG2]).reshape((2, 1))
        return dG

    logging.debug('doing least squares optimization')
    output = scipy.optimize.least_squares(
        residual,
        tau_ratio0,  #jac=jacobian,
        bounds=(0.01, 100),  #bounds=(0, float('inf')), 
        ftol=1e-6,
        xtol=1e-6,
        gtol=1e-6)
    tau_ratio = output.x[0]
    tau_ratio = 1
    # mixture quantitities
    f12, f21, u12, T12 = distributions.equilibrium_maxwellian3D(
        n1,
        n2,
        u1,
        u2,
        T1,
        T2,
        m1,
        m2,
        vx1,
        vy1,
        vz1,
        vx2,
        vy2,
        vz2,
        tau_ratio,
        return_all=True)

    # dH in the kinetic sense
    dH12_bgk = (triple_integral(f12 * np.log(f1), vx1, vy1, vz1) -
                triple_integral(f1 * np.log(f1), vx1, vy1, vz1))
    dH21_bgk = (triple_integral(f21 * np.log(f2), vx2, vy2, vz2) -
                triple_integral(f2 * np.log(f2), vx2, vy2, vz2))
    tau12 = ((dHdt12 * dH12_bgk + tau_ratio * dHdt21 * dH21_bgk) /
             (dHdt12**2 + dHdt21**2))
    tau21 = tau_ratio * tau12
    error = abs(output.fun)  # / np.array([dHdt12, dHdt21]))
    logging.debug('least squares terminated due to: ' + output.message)

    return tau12, tau21, error
예제 #4
0
    def jacobian(tau_ratio):
        ''' ANALYTICAL JACOBIAN DOES NOT CURRENTLY SEEM TO WORK '''
        tau_ratio = tau_ratio[0]

        # mixture quantitities
        f12, f21, u12, T12 = distributions.equilibrium_maxwellian3D(
            n1,
            n2,
            u1,
            u2,
            T1,
            T2,
            m1,
            m2,
            vx1,
            vy1,
            vz1,
            vx2,
            vy2,
            vz2,
            tau_ratio,
            return_all=True)
        A12 = n1 * (m1 / (2. * np.pi * T12))**(3. / 2.)
        A21 = n2 * (m2 / (2. * np.pi * T12))**(3. / 2.)
        # dH in the kinetic sense
        dH12_bgk = (triple_integral(f12 * np.log(f1), vx1, vy1, vz1) -
                    triple_integral(f1 * np.log(f1), vx1, vy1, vz1))
        dH21_bgk = (triple_integral(f21 * np.log(f2), vx2, vy2, vz2) -
                    triple_integral(f2 * np.log(f2), vx2, vy2, vz2))
        tau12 = (((dH12_bgk * dHdt21 * tau_ratio)**2 +
                  (dH21_bgk * dHdt12)**2) /
                 (dHdt12 * dHdt21 * tau_ratio *
                  (dH12_bgk * dHdt21 * tau_ratio + dH21_bgk * dHdt12)))

        # integrals
        vsq1 = (vx1 - u12[0])**2 + (vy1 - u12[1])**2 + (vz1 - u12[2])**2
        f1logf1 = triple_integral(f1 * np.log(f1), vx1, vy1, vz1)
        f12logf1 = triple_integral(f12 * np.log(f1), vx1, vy1, vz1)
        vf12logf1 = np.array([
            triple_integral((vx1 - u12[0]) * f12 * np.log(f1), vx1, vy1, vz1),
            triple_integral((vy1 - u12[1]) * f12 * np.log(f1), vx1, vy1, vz1),
            triple_integral((vz1 - u12[2]) * f12 * np.log(f1), vx1, vy1, vz1)
        ])
        vsqf12logf1 = triple_integral(vsq1 * f12 * np.log(f1), vx1, vy1, vz1)
        vsq2 = (vx2 - u12[0])**2 + (vy2 - u12[1])**2 + (vz2 - u12[2])**2
        f2logf2 = triple_integral(f2 * np.log(f2), vx2, vy2, vz2)
        f21logf2 = triple_integral(f21 * np.log(f2), vx2, vy2, vz2)
        vf21logf2 = np.array([
            triple_integral((vx2 - u12[0]) * f21 * np.log(f2), vx2, vy2, vz2),
            triple_integral((vy2 - u12[1]) * f21 * np.log(f2), vx2, vy2, vz2),
            triple_integral((vz2 - u12[2]) * f21 * np.log(f2), vx2, vy2, vz2)
        ])
        vsqf21logf2 = triple_integral(vsq2 * f21 * np.log(f2), vx2, vy2, vz2)

        # derivatives
        du12 = ((n1 * n2 * m1 * m2) / (n1 * m1 * tau_ratio + n2 * m2)**2 *
                (u1 - u2))
        dT12 = (1. / (3. * tau_ratio * n1 + 3. * n2)**2 *
                (9. * n1 * n2 * (T1 - T2) + 3. * n1 * n2 *
                 (m1 * np.sum(u1**2 - u12**2) - m2 * np.sum(u2**2 - u12**2))) -
                (2. * m1 * m2 * n1 * n2 *
                 (tau_ratio * m1 * n1 * np.sum(u1**2 - u1 * u2) +
                  m2 * n2 * np.sum(u1 * u2 - u2**2))) /
                ((3. * tau_ratio * n1 + 3. * n2) *
                 (tau_ratio * m1 * n1 + m2 * n2)**2))
        dA12 = (-3. * n1 / 2. * (m1 / (2. * np.pi))**(3. / 2.) *
                T12**(-5. / 2.) * dT12)
        dA21 = (-3. * n2 / 2. * (m2 / (2. * np.pi))**(3. / 2.) *
                T12**(-5. / 2.) * dT12)
        dtau12 = (dH21_bgk *
                  ((dH12_bgk * dHdt21 * tau_ratio)**2 -
                   2 * tau_ratio * dH12_bgk * dH21_bgk * dHdt12 * dHdt21 -
                   (dH21_bgk * dHdt12)**2) /
                  (dHdt21 * tau_ratio**2 *
                   ((dH12_bgk * dHdt21 * tau_ratio)**2 +
                    2 * tau_ratio * dH12_bgk * dH21_bgk * dHdt12 * dHdt21 +
                    (dH21_bgk * dHdt12)**2)))
        dH12 = (f12logf1 * dA12 / A12 + np.sum(vf12logf1 * m1 / T12 * du12) +
                vsqf12logf1 * m1 / (2. * T12**2) * dT12)
        dH21 = (f21logf2 * dA21 / A21 + np.sum(vf21logf2 * m2 / T12 * du12) +
                vsqf21logf2 * m2 / (2. * T12**2) * dT12)
        dH12dt_bgk = f12logf1 - f1logf1
        dH21dt_bgk = f21logf2 - f2logf2
        dG1 = (1. / tau12**2 * dH12dt_bgk / dHdt12 * dtau12 - 1. /
               (tau12 * dHdt12) * dH12)
        dG2 = ((1. / (tau_ratio**2 * tau12) + 1. /
                (tau_ratio * tau12**2) * dtau12) * dH21dt_bgk / dHdt21 - 1. /
               (tau_ratio * tau12 * dHdt21) * dH21)
        dG = np.array([dG1, dG2]).reshape((2, 1))
        return dG