Exemple #1
0
def tabulate_solid(chemical, Tmin=None, Tmax=None, pts=10):
    pd = pandas()
    chem = Chemical(chemical)

    (rhos, Cps) = [[] for i in range(2)]
    if not Tmin:  # pragma: no cover
        if chem.Tm:
            Tmin = min(chem.Tm-100, 1e-2)
        else:
            Tmin = 150.
    if not Tmax:  # pragma: no cover
        if chem.Tm:
            Tmax = chem.Tm
        else:
            Tmax = 350

    Ts = np.linspace(Tmin, Tmax, pts)
    for T in Ts:
        chem = Chemical(chemical, T=T)
        rhos.append(chem.rhos)
        Cps.append(chem.Cps)

    data = OrderedDict()
    data['Density, kg/m^3'] = rhos
    data['Constant-pressure heat capacity, J/kg/K'] = Cps

    df = pd.DataFrame(data, index=Ts)
    df.index.name = 'T, K'
    return df
Exemple #2
0
def test_bisplev():
    from fluids.numerics import bisplev as my_bisplev
    from scipy.interpolate import bisplev

    tck = [
        np.array([
            0.0, 0.0, 0.0, 0.0, 0.0213694, 0.0552542, 0.144818, 0.347109,
            0.743614, 0.743614, 0.743614, 0.743614
        ]),
        np.array([0.0, 0.0, 0.25, 0.5, 0.75, 1.0, 1.0]),
        np.array([
            1.0001228445490002, 0.9988161050974387, 0.9987070557919563,
            0.9979385859402731, 0.9970983069823832, 0.96602540121758,
            0.955136014969614, 0.9476842472211648, 0.9351143114374392,
            0.9059649602818451, 0.9218915266550902, 0.9086000082864022,
            0.8934758292610783, 0.8737960765592091, 0.83185251064324,
            0.8664296734965998, 0.8349705397843921, 0.809133298969704,
            0.7752206120745123, 0.7344035693011536, 0.817047920445813,
            0.7694560150930563, 0.7250979336267909, 0.6766754605968431,
            0.629304180420512, 0.7137237030611423, 0.6408238328161417,
            0.5772000233279148, 0.504889627280836, 0.440579886434288,
            0.6239736474980684, 0.5273646894226224, 0.43995388722059986,
            0.34359277007615313, 0.26986439252143746, 0.5640689738382749,
            0.4540959882735219, 0.35278120580740957, 0.24364672351604122,
            0.1606942128340308
        ]), 3, 1
    ]
    my_tck = [
        tck[0].tolist(), tck[1].tolist(), tck[2].tolist(), tck[3], tck[4]
    ]

    xs = np.linspace(0, 1, 10)
    zs = np.linspace(0, 1, 10)

    ys_scipy = bisplev(xs, zs, tck)
    ys = my_bisplev(xs, zs, my_tck)
    assert_allclose(ys, ys_scipy)

    ys_scipy = bisplev(0.5, .7, tck)
    ys = my_bisplev(.5, .7, my_tck)
    assert_allclose(ys, ys_scipy)
Exemple #3
0
def test_linspace():
    from fluids.numerics import linspace
    calc = linspace(-3, 10, endpoint=True, num=8)
    expect = np.linspace(-3, 10, endpoint=True, num=8)
    assert_allclose(calc, expect)

    calc = linspace(-3, 10, endpoint=False, num=20)
    expect = np.linspace(-3, 10, endpoint=False, num=20)
    assert_allclose(calc, expect)

    calc = linspace(0, 1e-10, endpoint=False, num=3)
    expect = np.linspace(0, 1e-10, endpoint=False, num=3)
    assert_allclose(calc, expect)

    calc = linspace(0, 1e-10, endpoint=False, num=2)
    expect = np.linspace(0, 1e-10, endpoint=False, num=2)
    assert_allclose(calc, expect)

    calc = linspace(0, 1e-10, endpoint=False, num=1)
    expect = np.linspace(0, 1e-10, endpoint=False, num=1)
    assert_allclose(calc, expect)

    calc, calc_step = linspace(0, 1e-10, endpoint=False, num=2, retstep=True)
    expect, expect_step = np.linspace(0,
                                      1e-10,
                                      endpoint=False,
                                      num=2,
                                      retstep=True)
    assert_allclose(calc, expect)
    assert_allclose(calc_step, expect_step)

    calc, calc_step = linspace(0, 1e-10, endpoint=False, num=1, retstep=True)
    expect, expect_step = np.linspace(0,
                                      1e-10,
                                      endpoint=False,
                                      num=1,
                                      retstep=True)
    assert_allclose(calc, expect)
    assert isnan(calc_step)
    # Cannot compare against numpy expect_step - it did not use to give nan in older versions

    calc, calc_step = linspace(100, 1000, endpoint=False, num=21, retstep=True)
    expect, expect_step = np.linspace(100,
                                      1000,
                                      endpoint=False,
                                      num=21,
                                      retstep=True)
    assert_allclose(calc, expect)
    assert_allclose(calc_step, expect_step)
Exemple #4
0
def tabulate_liq(chemical, Tmin=None, Tmax=None, pts=10):
    pd = pandas()
    chem = Chemical(chemical)

    (rhos, Cps, mugs, kgs, Prs, alphas, isobarics, JTs, Psats, sigmas, Hvaps,
     permittivities) = [[] for i in range(12)]
    if not Tmin:  # pragma: no cover
        if chem.Tm:
            Tmin = chem.Tm
        else:
            Tmin = 273.15
    if not Tmax:  # pragma: no cover
        if chem.Tc:
            Tmax = chem.Tc
        else:
            Tmax = 450

    Ts = np.linspace(Tmin, Tmax, pts)
    for T in Ts:
        chem = Chemical(chemical, T=T)

        rhos.append(chem.rhol)
        Cps.append(chem.Cpl)
        mugs.append(chem.mul)
        kgs.append(chem.kl)
        Prs.append(chem.Prl)
        alphas.append(chem.alphal)
        isobarics.append(chem.isobaric_expansion_l)
        JTs.append(chem.JTg)
        Psats.append(chem.Psat)
        Hvaps.append(chem.Hvap)
        sigmas.append(chem.sigma)
        permittivities.append(chem.permittivity)

    data = OrderedDict()
    data['Saturation pressure, Pa'] = Psats
    data['Density, kg/m^3'] = rhos
    data['Constant-pressure heat capacity, J/kg/K'] = Cps
    data['Heat of vaporization, J/kg'] = Hvaps
    data['Viscosity, Pa*s'] = mugs
    data['Thermal conductivity, W/m/K'] = kgs
    data['Surface tension, N/m'] = sigmas
    data['Prandtl number'] = Prs
    data['Thermal diffusivity, m^2/s'] = alphas
    data['Isobaric expansion, 1/K'] = isobarics
    data['Joule-Thompson expansion coefficient, K/Pa'] = JTs
    data['PermittivityLiquid'] = permittivities

    df = pd.DataFrame(data, index=Ts)
    df.index.name = 'T, K'
    return df
Exemple #5
0
    def validate_prop(self, prop, phase, evaluated_points=30):
        phase_key = '_g' if phase == 'g' else '_l'
        name = prop + phase_key
        if prop in ['CP0MOLAR']:
            name = prop
        pts = getattr(self, name).points
        predictor = getattr(self, name)
        for i in range(len(pts) - 1):
            Ts = np.linspace(pts[i], pts[i + 1], evaluated_points)
            #            print(Ts[0], Ts[-1])
            prop_approx = [predictor(T) for T in Ts]
            prop_calc = [
                CoolProp_T_dependent_property(T, self.CAS, prop, phase)
                for T in Ts
            ]
            #            print(prop_approx)
            #            print(prop_calc)
            # The approximators do give differences at the very low value side
            # so we need atol=1E-9
            #            print(prop, self.CAS, prop_approx[0], prop_calc[0])

            try:
                assert_close1d(prop_approx, prop_calc, rtol=1E-7, atol=1E-9)
            except:
                '''There are several cases that assert_allclose doesn't deal
                with well for some reason. We could increase rtol, but instead
                the relative errors are used here to check everything is as desidred.

                Some example errors this won't trip on but assert_allclose does
                are:

                1.00014278827e-08
                1.62767956613e-06
                -0.0
                -1.63895899641e-16
                -4.93284549625e-15
                '''
                prop_calc = np.array(prop_calc)
                prop_approx = np.array(prop_approx)
                errs = abs((prop_calc - prop_approx) / prop_calc)
                try:
                    assert max(errs) < 2E-6
                except:
                    print(
                        '%s %s failed with mean relative error %s and maximum relative error %s'
                        % (self.CAS, prop, str(np.mean(errs)), str(max(errs))))
Exemple #6
0
def tabulate_gas(chemical, Tmin=None, Tmax=None, pts=10):
    chem = Chemical(chemical)
    pd = pandas()

    (rhos, Cps, Cvs, mugs, kgs, Prs, alphas, isobarics, isentropics,
     JTs) = [[] for i in range(10)]
    if not Tmin:  # pragma: no cover
        if chem.Tm:
            Tmin = chem.Tm
        else:
            Tmin = 273.15
    if not Tmax:  # pragma: no cover
        if chem.Tc:
            Tmax = chem.Tc
        else:
            Tmax = 450

    Ts = np.linspace(Tmin, Tmax, pts)
    for T in Ts:
        chem = Chemical(chemical, T=T)

        rhos.append(chem.rhog)
        Cps.append(chem.Cpg)
        Cvs.append(chem.Cvg)
        mugs.append(chem.mug)
        kgs.append(chem.kg)
        Prs.append(chem.Prg)
        alphas.append(chem.alphag)
        isobarics.append(chem.isobaric_expansion_g)
        isentropics.append(chem.isentropic_exponent)
        JTs.append(chem.JTg)
    data = OrderedDict()
    data['Density, kg/m^3'] = rhos
    data['Constant-pressure heat capacity, J/kg/K'] = Cps
    data['Constant-volume heat capacity, J/kg/K'] = Cvs
    data['Viscosity, Pa*s'] = mugs
    data['Thermal conductivity, W/m/K'] = kgs
    data['Prandtl number'] = Prs
    data['Thermal diffusivity, m^2/s'] = alphas
    data['Isobaric expansion, 1/K'] = isobarics
    data['Isentropic exponent'] = isentropics
    data['Joule-Thompson expansion coefficient, K/Pa'] = JTs

    df = pd.DataFrame(data, index=Ts)  # add orient='index'
    df.index.name = 'T, K'
    return df
Exemple #7
0
def test_diff():
    from fluids.numerics import diff

    test_arrs = [
        np.ones(10),
        np.zeros(10),
        np.arange(1, 10),
        np.arange(1, 10) * 25.1241251, (np.arange(1, 10)**1.2),
        (10.1 + np.arange(1, 10)**20), (10.1 + np.linspace(-100, -10, 9)),
        (np.logspace(-10, -100, 19)**1.241), (np.logspace(10, 100, 15)**1.241)
    ]
    for test_arr in test_arrs:
        arr = test_arr.tolist()
        for n in range(5):
            diff_np = np.diff(arr, n=n)
            diff_py = diff(arr, n=n)
            assert_allclose(diff_np, diff_py)

    assert tuple(diff([1, 2, 3], n=0)) == tuple([1, 2, 3])
    with pytest.raises(Exception):
        diff([1, 2, 3], n=-1)
Exemple #8
0
def test_splev():
    from fluids.numerics import py_splev
    from scipy.interpolate import splev
    # Originally Dukler_XA_tck
    tck = [
        np.array([
            -2.4791105294648372, -2.4791105294648372, -2.4791105294648372,
            -2.4791105294648372, 0.14360803483759585, 1.7199938263676038,
            1.7199938263676038, 1.7199938263676038, 1.7199938263676038
        ]),
        np.array([
            0.21299880246561081, 0.16299733301915248, -0.042340970712679615,
            -1.9967836909384598, -2.9917366639619414, 0.0, 0.0, 0.0, 0.0
        ]), 3
    ]
    my_tck = [tck[0].tolist(), tck[1].tolist(), tck[2]]

    xs = np.linspace(-3, 2, 100).tolist()

    # test extrapolation
    ys_scipy = splev(xs, tck, ext=0)
    ys = [py_splev(xi, my_tck, ext=0) for xi in xs]
    assert_allclose(ys, ys_scipy)

    # test truncating to side values
    ys_scipy = splev(xs, tck, ext=3)
    ys = [py_splev(xi, my_tck, ext=3) for xi in xs]
    assert_allclose(ys, ys_scipy)

    # Test returning zeros for bad values
    ys_scipy = splev(xs, tck, ext=1)
    ys = [py_splev(xi, my_tck, ext=1) for xi in xs]
    assert_allclose(ys, ys_scipy)

    # Test raising an error when extrapolating is not allowed
    with pytest.raises(ValueError):
        py_splev(xs[0], my_tck, ext=2)
    with pytest.raises(ValueError):
        splev(xs[0], my_tck, ext=2)
Exemple #9
0
def test_interp():
    from fluids.numerics import interp
    # Real world test data
    a = [
        0.29916, 0.29947, 0.31239, 0.31901, 0.32658, 0.33729, 0.34202, 0.34706,
        0.35903, 0.36596, 0.37258, 0.38487, 0.38581, 0.40125, 0.40535, 0.41574,
        0.42425, 0.43401, 0.44788, 0.45259, 0.47181, 0.47309, 0.49354, 0.49924,
        0.51653, 0.5238, 0.53763, 0.54806, 0.55684, 0.57389, 0.58235, 0.59782,
        0.60156, 0.62265, 0.62649, 0.64948, 0.65099, 0.6687, 0.67587, 0.68855,
        0.69318, 0.70618, 0.71333, 0.72351, 0.74954, 0.74965
    ]
    b = [
        0.164534, 0.164504, 0.163591, 0.163508, 0.163439, 0.162652, 0.162224,
        0.161866, 0.161238, 0.160786, 0.160295, 0.15928, 0.159193, 0.157776,
        0.157467, 0.156517, 0.155323, 0.153835, 0.151862, 0.151154, 0.14784,
        0.147613, 0.144052, 0.14305, 0.140107, 0.138981, 0.136794, 0.134737,
        0.132847, 0.129303, 0.127637, 0.124758, 0.124006, 0.119269, 0.118449,
        0.113605, 0.113269, 0.108995, 0.107109, 0.103688, 0.102529, 0.099567,
        0.097791, 0.095055, 0.087681, 0.087648
    ]

    xs = np.linspace(0.29, 0.76, 100)
    ys = [interp(xi, a, b) for xi in xs.tolist()]
    ys_numpy = np.interp(xs, a, b)
    assert_allclose(ys, ys_numpy, atol=1e-12, rtol=1e-11)

    # Test custom extrapolation method
    xs = [1, 2, 3]
    ys = [.1, .2, .3]
    assert_close(interp(3.5, xs, ys, extrapolate=True), .35, rtol=1e-15)
    assert_close(interp(0, xs, ys, extrapolate=True), 0, rtol=1e-15)
    assert_close(interp(-1, xs, ys, extrapolate=True), -.1, rtol=1e-15)
    assert_close(interp(-100, xs, ys, extrapolate=True), -10, rtol=1e-15)
    assert_close(interp(10, xs, ys, extrapolate=True), 1, rtol=1e-15)
    assert_close(interp(10.0**30, xs, ys, extrapolate=True),
                 10.0**29,
                 rtol=1e-15)
Exemple #10
0
def integrate_drag_sphere(D,
                          rhop,
                          rho,
                          mu,
                          t,
                          V=0,
                          Method=None,
                          distance=False):
    r'''Integrates the velocity and distance traveled by a particle moving
    at a speed which will converge to its terminal velocity.

    Performs an integration of the following expression for acceleration:

    .. math::
        a = \frac{g(\rho_p-\rho_f)}{\rho_p} - \frac{3C_D \rho_f u^2}{4D \rho_p}

    Parameters
    ----------
    D : float
        Diameter of the sphere, [m]
    rhop : float
        Particle density, [kg/m^3]
    rho : float
        Density of the surrounding fluid, [kg/m^3]
    mu : float
        Viscosity of the surrounding fluid [Pa*s]
    t : float
        Time to integrate the particle to, [s]
    V : float
        Initial velocity of the particle, [m/s]
    Method : string, optional
        A string of the function name to use, as in the dictionary
        drag_sphere_correlations
    distance : bool, optional
        Whether or not to calculate the distance traveled and return it as
        well

    Returns
    -------
    v : float
        Velocity of falling sphere after time `t` [m/s]
    x : float, returned only if `distance` == True
        Distance traveled by the falling sphere in time `t`, [m]

    Notes
    -----
    This can be relatively slow as drag correlations can be complex.
    
    There are analytical solutions available for the Stokes law regime (Re < 
    0.3). They were obtained from Wolfram Alpha. [1]_ was not used in the
    derivation, but also describes the derivation fully.
    
    .. math::
        V(t) = \frac{\exp(-at) (V_0 a + b(\exp(at) - 1))}{a}
        
    .. math::
        x(t) = \frac{\exp(-a t)\left[V_0 a(\exp(a t) - 1) + b\exp(a t)(a t-1) 
        + b\right]}{a^2}
    
    .. math::
        a = \frac{18\mu_f}{D^2\rho_p}
    
    .. math::
        b = \frac{g(\rho_p-\rho_f)}{\rho_p}
        
    The analytical solution will automatically be used if the initial and 
    terminal velocity is show the particle's behavior to be laminar. Note
    that this behavior requires that the terminal velocity of the particle be
    solved for - this adds slight (1%) overhead for the cases where particles 
    are not laminar.

    Examples
    --------
    >>> integrate_drag_sphere(D=0.001, rhop=2200., rho=1.2, mu=1.78E-5, t=0.5,
    ... V=30, distance=True) 
    (9.686465044053476, 7.8294546436299175)
    
    References
    ----------
    .. [1] Timmerman, Peter, and Jacobus P. van der Weele. "On the Rise and 
       Fall of a Ball with Linear or Quadratic Drag." American Journal of 
       Physics 67, no. 6 (June 1999): 538-46. https://doi.org/10.1119/1.19320.
    '''
    laminar_initial = Reynolds(V=V, rho=rho, D=D, mu=mu) < 0.01
    v_laminar_end_assumed = v_terminal(D=D,
                                       rhop=rhop,
                                       rho=rho,
                                       mu=mu,
                                       Method=Method)
    laminar_end = Reynolds(V=v_laminar_end_assumed, rho=rho, D=D, mu=mu) < 0.01
    if Method == 'Stokes' or (laminar_initial and laminar_end
                              and Method is None):
        try:
            t1 = 18.0 * mu / (D * D * rhop)
            t2 = g * (rhop - rho) / rhop
            V_end = exp(-t1 * t) * (t1 * V + t2 * (exp(t1 * t) - 1.0)) / t1
            x_end = exp(-t1 * t) * (V * t1 *
                                    (exp(t1 * t) - 1.0) + t2 * exp(t1 * t) *
                                    (t1 * t - 1.0) + t2) / (t1 * t1)
            if distance:
                return V_end, x_end
            else:
                return V_end
        except OverflowError:
            # It is only necessary to integrate to terminal velocity
            t_to_terminal = time_v_terminal_Stokes(D,
                                                   rhop,
                                                   rho,
                                                   mu,
                                                   V0=V,
                                                   tol=1e-9)
            if t_to_terminal > t:
                raise Exception('Should never happen')
            V_end, x_end = integrate_drag_sphere(D=D,
                                                 rhop=rhop,
                                                 rho=rho,
                                                 mu=mu,
                                                 t=t_to_terminal,
                                                 V=V,
                                                 Method='Stokes',
                                                 distance=True)
            # terminal velocity has been reached - V does not change, but x does
            # No reason to believe this isn't working even though it isn't
            # matching the ode solver
            if distance:
                return V_end, x_end + V_end * (t - t_to_terminal)
            else:
                return V_end

            # This is a serious problem for small diameters
            # It would be possible to step slowly, using smaller increments
            # of time to avlid overflows. However, this unfortunately quickly
            # gets much, exponentially, slower than just using odeint because
            # for example solving 10000 seconds might require steps of .0001
            # seconds at a diameter of 1e-7 meters.
#            x = 0.0
#            subdivisions = 10
#            dt = t/subdivisions
#            for i in range(subdivisions):
#                V, dx = integrate_drag_sphere(D=D, rhop=rhop, rho=rho, mu=mu,
#                                              t=dt, V=V, distance=True,
#                                              Method=Method)
#                x += dx
#            if distance:
#                return V, x
#            else:
#                return V

    Re_ish = rho * D / mu
    c1 = g * (rhop - rho) / rhop
    c2 = -0.75 * rho / (D * rhop)

    def dv_dt(V, t):
        if V == 0:
            # 64/Re goes to infinity, but gets multiplied by 0 squared.
            t2 = 0.0
        else:
            #            t2 = c2*V*V*Stokes(Re_ish*V)
            t2 = c2 * V * V * drag_sphere(Re_ish * V, Method=Method)
        return c1 + t2

    # Number of intervals for the solution to be solved for; the integrator
    # doesn't care what we give it, but a large number of intervals are needed
    # For an accurate integration of the particle's distance traveled
    pts = 1000 if distance else 2
    ts = np.linspace(0, t, pts)

    # Delayed import of necessaray functions
    from scipy.integrate import odeint, cumtrapz

    # Perform the integration
    Vs = odeint(dv_dt, [V], ts)
    #
    V_end = float(Vs[-1])
    if distance:
        # Calculate the distance traveled
        x = float(cumtrapz(np.ravel(Vs), ts)[-1])
        return V_end, x
    else:
        return V_end
Exemple #11
0
def integrate_drag_sphere(D, rhop, rho, mu, t, V=0, Method=None,
                          distance=False):
    r'''Integrates the velocity and distance traveled by a particle moving
    at a speed which will converge to its terminal velocity.

    Performs an integration of the following expression for acceleration:

    .. math::
        a = \frac{g(\rho_p-\rho_f)}{\rho_p} - \frac{3C_D \rho_f u^2}{4D \rho_p}

    Parameters
    ----------
    D : float
        Diameter of the sphere, [m]
    rhop : float
        Particle density, [kg/m^3]
    rho : float
        Density of the surrounding fluid, [kg/m^3]
    mu : float
        Viscosity of the surrounding fluid [Pa*s]
    t : float
        Time to integrate the particle to, [s]
    V : float
        Initial velocity of the particle, [m/s]
    Method : string, optional
        A string of the function name to use, as in the dictionary
        drag_sphere_correlations
    distance : bool, optional
        Whether or not to calculate the distance traveled and return it as
        well

    Returns
    -------
    v : float
        Velocity of falling sphere after time `t` [m/s]
    x : float, returned only if `distance` == True
        Distance traveled by the falling sphere in time `t`, [m]

    Notes
    -----
    This can be relatively slow as drag correlations can be complex.
    
    There are analytical solutions available for the Stokes law regime (Re < 
    0.3). They were obtained from Wolfram Alpha. [1]_ was not used in the
    derivation, but also describes the derivation fully.
    
    .. math::
        V(t) = \frac{\exp(-at) (V_0 a + b(\exp(at) - 1))}{a}
        
    .. math::
        x(t) = \frac{\exp(-a t)\left[V_0 a(\exp(a t) - 1) + b\exp(a t)(a t-1) 
        + b\right]}{a^2}
    
    .. math::
        a = \frac{18\mu_f}{D^2\rho_p}
    
    .. math::
        b = \frac{g(\rho_p-\rho_f)}{\rho_p}
        
    The analytical solution will automatically be used if the initial and 
    terminal velocity is show the particle's behavior to be laminar. Note
    that this behavior requires that the terminal velocity of the particle be
    solved for - this adds slight (1%) overhead for the cases where particles 
    are not laminar.

    Examples
    --------
    >>> integrate_drag_sphere(D=0.001, rhop=2200., rho=1.2, mu=1.78E-5, t=0.5,
    ... V=30, distance=True) 
    (9.686465044053476, 7.8294546436299175)
    
    References
    ----------
    .. [1] Timmerman, Peter, and Jacobus P. van der Weele. "On the Rise and 
       Fall of a Ball with Linear or Quadratic Drag." American Journal of 
       Physics 67, no. 6 (June 1999): 538-46. https://doi.org/10.1119/1.19320.
    '''
    laminar_initial = Reynolds(V=V, rho=rho, D=D, mu=mu) < 0.01
    v_laminar_end_assumed = v_terminal(D=D, rhop=rhop, rho=rho, mu=mu, Method=Method)
    laminar_end = Reynolds(V=v_laminar_end_assumed, rho=rho, D=D, mu=mu) < 0.01
    if Method == 'Stokes' or (laminar_initial and laminar_end and Method is None):
        try:
            t1 = 18.0*mu/(D*D*rhop)
            t2 = g*(rhop-rho)/rhop
            V_end = exp(-t1*t)*(t1*V + t2*(exp(t1*t) - 1.0))/t1
            x_end = exp(-t1*t)*(V*t1*(exp(t1*t) - 1.0) + t2*exp(t1*t)*(t1*t - 1.0) + t2)/(t1*t1)
            if distance:
                return V_end, x_end
            else:
                return V_end
        except OverflowError:
            # It is only necessary to integrate to terminal velocity
            t_to_terminal = time_v_terminal_Stokes(D, rhop, rho, mu, V0=V, tol=1e-9)
            if t_to_terminal > t:
                raise Exception('Should never happen')
            V_end, x_end = integrate_drag_sphere(D=D, rhop=rhop, rho=rho, mu=mu, t=t_to_terminal, V=V, Method='Stokes', distance=True)
            # terminal velocity has been reached - V does not change, but x does
            # No reason to believe this isn't working even though it isn't
            # matching the ode solver
            if distance:
                return V_end, x_end + V_end*(t - t_to_terminal)
            else:
                return V_end
            
            # This is a serious problem for small diameters
            # It would be possible to step slowly, using smaller increments
            # of time to avlid overflows. However, this unfortunately quickly
            # gets much, exponentially, slower than just using odeint because
            # for example solving 10000 seconds might require steps of .0001
            # seconds at a diameter of 1e-7 meters.
#            x = 0.0
#            subdivisions = 10
#            dt = t/subdivisions
#            for i in range(subdivisions):
#                V, dx = integrate_drag_sphere(D=D, rhop=rhop, rho=rho, mu=mu,
#                                              t=dt, V=V, distance=True,
#                                              Method=Method)
#                x += dx
#            if distance:
#                return V, x
#            else:
#                return V
    
    Re_ish = rho*D/mu
    c1 = g*(rhop-rho)/rhop
    c2 = -0.75*rho/(D*rhop)

    def dv_dt(V, t):
        if V == 0:
            # 64/Re goes to infinity, but gets multiplied by 0 squared.
            t2 = 0.0
        else:
#            t2 = c2*V*V*Stokes(Re_ish*V)
            t2 = c2*V*V*drag_sphere(Re_ish*V, Method=Method)
        return c1 + t2

    # Number of intervals for the solution to be solved for; the integrator
    # doesn't care what we give it, but a large number of intervals are needed
    # For an accurate integration of the particle's distance traveled
    pts = 1000 if distance else 2
    ts = np.linspace(0, t, pts)
    
    # Delayed import of necessaray functions
    from scipy.integrate import odeint, cumtrapz

    # Perform the integration
    Vs = odeint(dv_dt, [V], ts)
    #
    V_end = float(Vs[-1])
    if distance:
        # Calculate the distance traveled
        x = float(cumtrapz(np.ravel(Vs), ts)[-1])
        return V_end, x
    else:
        return V_end