Beispiel #1
0
def test_ic():
    """
    Test the initial conditions function for the single bubble model
    
    Test that the initial conditions returned by `sbm_ic` are correct based
    on the input and expected output
    
    """
    # Set up the inputs
    profile = get_profile()
    T0 = 273.15 + 15.
    z0 = 1500.
    P = profile.get_values(z0, ['pressure'])
    composition = ['methane', 'ethane', 'propane', 'oxygen']
    bub = dbm.FluidParticle(composition)
    yk = np.array([0.85, 0.07, 0.08, 0.0])
    de = 0.005
    K = 1.
    K_T = 1.
    fdis = 1.e-4
    t_hyd = 0.
    lag_time = True

    # Get the initial conditions
    (bub_obj, y0) = single_bubble_model.sbm_ic(profile, bub,
                                               np.array([0., 0.,
                                                         z0]), de, yk, T0, K,
                                               K_T, fdis, t_hyd, lag_time)

    # Check the initial condition values
    assert y0[0] == 0.
    assert y0[1] == 0.
    assert y0[2] == z0
    assert y0[-1] == T0 * np.sum(y0[3:-1]) * seawater.cp() * 0.5
    assert_approx_equal(bub.diameter(y0[3:-1], T0, P), de, significant=6)

    # Check the bub_obj parameters
    for i in range(len(composition)):
        assert bub_obj.composition[i] == composition[i]
    assert bub_obj.T0 == T0
    assert bub_obj.cp == seawater.cp() * 0.5
    assert bub_obj.K == K
    assert bub_obj.K_T == K_T
    assert bub_obj.fdis == fdis
    assert bub_obj.t_hyd == t_hyd
    for i in range(len(composition) - 1):
        assert bub_obj.diss_indices[i] == True
    assert bub_obj.diss_indices[-1] == False
Beispiel #2
0
def crossflow_plume(fig):
    """
    Define, run, and plot the simulations for a pure bubble plume in crossflow
    for validation to data in Socolofsky and Adams (2002).
    
    """
    # Jet initial conditions
    z0 = 0.64
    U0 = 0.
    phi_0 = -np.pi / 2.
    theta_0 = 0.
    D = 0.01
    Tj = 21. + 273.15
    Sj = 0.
    cj = 1.
    chem_name = 'tracer'

    # Ambient conditions
    ua = 0.15
    T = 0.
    F = 0.
    H = 1.0

    # Create the correct ambient profile data
    uj = U0 * np.cos(phi_0) * np.cos(theta_0)
    vj = U0 * np.cos(phi_0) * np.sin(theta_0)
    wj = U0 * np.sin(phi_0)
    profile_fname = './crossflow_plume.nc'
    profile = get_profile(profile_fname, z0, D, uj, vj, wj, Tj, Sj, ua, T, F,
                          1., H)

    # Create a bent plume model simulation object
    jlm = bpm.Model(profile)

    # Define the dispersed phase input to the model
    composition = ['nitrogen', 'oxygen', 'argon', 'carbon_dioxide']
    mol_frac = np.array([0.78084, 0.20946, 0.009340, 0.00036])
    air = dbm.FluidParticle(composition)
    particles = []

    # Large bubbles
    Q_N = 0.5 / 60. / 1000.
    de0 = 0.008
    T0 = Tj
    lambda_1 = 1.
    (m0, T0, nb0, P, Sa,
     Ta) = dispersed_phases.initial_conditions(profile, z0, air, mol_frac, Q_N,
                                               1, de0, T0)
    particles.append(
        bpm.Particle(0.,
                     0.,
                     z0,
                     air,
                     m0,
                     T0,
                     nb0,
                     lambda_1,
                     P,
                     Sa,
                     Ta,
                     K=1.,
                     K_T=1.,
                     fdis=1.e-6))

    # Small bubbles
    Q_N = 0.5 / 60. / 1000.
    de0 = 0.0003
    T0 = Tj
    lambda_1 = 1.
    (m0, T0, nb0, P, Sa,
     Ta) = dispersed_phases.initial_conditions(profile, z0, air, mol_frac, Q_N,
                                               1, de0, T0)
    particles.append(
        bpm.Particle(0.,
                     0.,
                     z0,
                     air,
                     m0,
                     T0,
                     nb0,
                     lambda_1,
                     P,
                     Sa,
                     Ta,
                     K=1.,
                     K_T=1.,
                     fdis=1.e-6))

    # Run the simulation
    jlm.simulate(np.array([0., 0., z0]),
                 D,
                 U0,
                 phi_0,
                 theta_0,
                 Sj,
                 Tj,
                 cj,
                 chem_name,
                 particles,
                 track=True,
                 dt_max=60.,
                 sd_max=100.)

    # Perpare variables for plotting
    xp = jlm.q[:, 7] / jlm.D
    yp = jlm.q[:, 9] / jlm.D

    plt.figure(fig)
    plt.clf()
    plt.show()

    ax1 = plt.subplot(111)
    ax1.plot(xp, yp, 'b-')
    ax1.set_xlabel('x / D')
    ax1.set_ylabel('z / D')
    ax1.invert_yaxis()
    ax1.grid(b=True, which='major', color='0.65', linestyle='-')

    plt.draw()

    return jlm
Beispiel #3
0
def test_objects():
    """
    Test the class instantiation functions to ensure proper creations of class
    instances.
    """
    # Define the properties of a simple fluid mixture
    comp = ['oxygen', 'nitrogen', 'carbon_dioxide']
    delta = np.zeros((3, 3))
    M = np.array([0.031998800000000001, 0.028013400000000001, 0.04401])
    Pc = np.array([5042827.4639999997, 3399806.1560000004, 7373999.99902408])
    Tc = np.array([154.57777777777773, 126.19999999999999, 304.12])
    omega = np.array([0.0216, 0.0372, 0.225])
    kh_0 = np.array(
        [4.1054468295090059e-07, 1.7417658031088084e-07, 1.47433500e-05])
    neg_dH_solR = np.array([1650.0, 1300.0, 2368.988311])
    nu_bar = np.array([3.20000000e-05, 3.3000000000000e-05, 3.20000000e-05])
    B = np.array(
        [4.2000000000000004e-06, 7.9000000000000006e-06, 5.00000000e-06])
    dE = np.array([18380.044045116938, 19636.083501503061, 16747.19275181])
    K_salt = np.array([0.000169, 0.0001834, 0.0001323])

    # Initiate a simple mixture from a composition list
    air = dbm.FluidMixture(comp)
    mixture_attributes(air, comp, 3)
    chem_properties(air, delta, M, Pc, Tc, omega, kh_0, neg_dH_solR, nu_bar, B,
                    dE, K_salt)

    bub = dbm.FluidParticle(comp)
    mixture_attributes(bub, comp, 3)
    chem_properties(bub, delta, M, Pc, Tc, omega, kh_0, neg_dH_solR, nu_bar, B,
                    dE, K_salt)

    # Initiate a simple mixture from a composition list with delta specified
    air = dbm.FluidMixture(comp, delta=delta)
    mixture_attributes(air, comp, 3)
    chem_properties(air, delta, M, Pc, Tc, omega, kh_0, neg_dH_solR, nu_bar, B,
                    dE, K_salt)

    bub = dbm.FluidParticle(comp, delta=delta)
    mixture_attributes(bub, comp, 3)
    chem_properties(bub, delta, M, Pc, Tc, omega, kh_0, neg_dH_solR, nu_bar, B,
                    dE, K_salt)

    # Define the properties of a single-component mixture
    comp = 'oxygen'
    delta = np.zeros((1, 1))
    M = np.array([0.031998800000000001])
    Pc = np.array([5042827.4639999997])
    Tc = np.array([154.57777777777773])
    omega = np.array([0.021600000000000001])
    kh_0 = np.array([4.1054468295090059e-07])
    neg_dH_solR = np.array([1650.0])
    nu_bar = np.array([3.1999999999999999e-05])
    B = np.array([4.2000000000000004e-06])
    dE = np.array([18380.044045116938])
    K_salt = np.array([0.000169])

    # Initiate a single-component mixture from a list
    o2 = dbm.FluidMixture([comp])
    mixture_attributes(o2, [comp], 1)
    chem_properties(o2, delta, M, Pc, Tc, omega, kh_0, neg_dH_solR, nu_bar, B,
                    dE, K_salt)

    bub = dbm.FluidParticle([comp])
    mixture_attributes(bub, [comp], 1)
    chem_properties(bub, delta, M, Pc, Tc, omega, kh_0, neg_dH_solR, nu_bar, B,
                    dE, K_salt)

    # Initiate a single-componment mixture from a string with delta specified
    o2 = dbm.FluidMixture(comp, delta)
    mixture_attributes(o2, [comp], 1)
    chem_properties(o2, delta, M, Pc, Tc, omega, kh_0, neg_dH_solR, nu_bar, B,
                    dE, K_salt)

    bub = dbm.FluidParticle(comp, delta)
    mixture_attributes(bub, [comp], 1)
    chem_properties(bub, delta, M, Pc, Tc, omega, kh_0, neg_dH_solR, nu_bar, B,
                    dE, K_salt)

    # Initiate a single-componet mixture from a string with scalar delta
    o2 = dbm.FluidMixture(comp, 0.)
    mixture_attributes(o2, [comp], 1)
    chem_properties(o2, delta, M, Pc, Tc, omega, kh_0, neg_dH_solR, nu_bar, B,
                    dE, K_salt)

    bub = dbm.FluidParticle(comp, 0.)
    mixture_attributes(bub, [comp], 1)
    chem_properties(bub, delta, M, Pc, Tc, omega, kh_0, neg_dH_solR, nu_bar, B,
                    dE, K_salt)

    # Define the properties of an inert fluid particle
    isfluid = True
    iscompressible = False
    rho_p = 870.
    gamma = 29.,
    beta = 0.0001
    co = 1.0e-9

    # Initiate an inert fluid particle with different combinations of input
    # variables
    oil = dbm.InsolubleParticle(isfluid, iscompressible)
    inert_attributes(oil, isfluid, iscompressible, 930., 30., 0.0007,
                     2.90075e-9)

    oil = dbm.InsolubleParticle(isfluid, iscompressible, rho_p=rho_p)
    inert_attributes(oil, isfluid, iscompressible, rho_p, 30., 0.0007,
                     2.90075e-9)

    oil = dbm.InsolubleParticle(isfluid, iscompressible, gamma=gamma)
    inert_attributes(oil, isfluid, iscompressible, 930., gamma, 0.0007,
                     2.90075e-9)

    oil = dbm.InsolubleParticle(isfluid, iscompressible, beta=beta)
    inert_attributes(oil, isfluid, iscompressible, 930., 30., beta, 2.90075e-9)

    oil = dbm.InsolubleParticle(isfluid, iscompressible, co=co)
    inert_attributes(oil, isfluid, iscompressible, 930., 30., 0.0007, co)

    oil = dbm.InsolubleParticle(isfluid, iscompressible, rho_p, gamma, beta,
                                co)
    inert_attributes(oil, isfluid, iscompressible, rho_p, gamma, beta, co)

    oil = dbm.InsolubleParticle(isfluid,
                                iscompressible,
                                beta=beta,
                                rho_p=rho_p,
                                gamma=gamma,
                                co=co)
    inert_attributes(oil, isfluid, iscompressible, rho_p, gamma, beta, co)
Beispiel #4
0
import numpy as np

if __name__ == '__main__':

    # Open an ambient profile object from the netCDF dataset
    nc = '../../test/output/test_bm54.nc'
    bm54 = ambient.Profile(nc, chem_names='all')
    bm54.close_nc()

    # Initialize a single_bubble_model.Model object with this data
    sbm = single_bubble_model.Model(bm54)

    # Create a light gas bubble to track
    composition = ['methane', 'ethane', 'propane', 'oxygen']
    bub = dbm.FluidParticle(composition, fp_type=0.)

    # Set the mole fractions of each component at release.
    mol_frac = np.array([0.95, 0.03, 0.02, 0.])

    # Specify the remaining particle initial conditions
    de = 0.005
    z0 = 1000.
    T0 = 273.15 + 30.
    fdis = 1.e-15

    # Also, use the hydrate model from Jun et al. (2015) to set the
    # hydrate shell formation time
    P = bm54.get_values(z0, 'pressure')
    m = bub.masses_by_diameter(de, T0, P, mol_frac)
    t_hyd = dispersed_phases.hydrate_formation_time(bub, z0, m, T0, bm54)
    def update_properties(self, profile, oil_mixture, m_mixture, z0, Tj=None):
        """
        Set the thermodynamic properties of the released and receiving fluids

        Store the density, viscosity, and interfacial tension of the fluids
        involved in a jet breakup scenario.

        Parameters
        ----------
        profile : `ambient.Profile` object
            Profile containing ambient CTD data
        oil_mixture : `dbm.FluidMixture` object
            A `dbm.FluidMixture` object that contains the chemical description
            of an oil mixture.
        m_mixture : ndarray
            An array of mass fluxes (kg/s) of each pseudo-component in the
            live-oil mixture.
        z0 : float
            Release point of the jet orifice (m)
        Tj : float
            Temperature of the released fluids (K)

        Notes
        -----
        This method allows the complete release to be redefined.  If you
        only want to update the release depth or release temperature, use
        `.update_z0()` or `update_Tj()`, instead.

        """
        # Set the flags initially to False
        self.sim_stored = False
        self.distribution_stored = False

        # Record the input parameters
        self.profile = profile
        self.oil_mixture = oil_mixture
        self.m_mixture = m_mixture
        self.z0 = z0
        self.Tj = Tj

        # Compute the properties of seawater
        self.T, self.S, self.P = self.profile.get_values(self.z0,
               ['temperature', 'salinity', 'pressure']
            )
        self.rho = seawater.density(self.T, self.S, self.P)
        self.mu = seawater.mu(self.T, self.S, self.P)

        # Set jet temperature either to ambient or input value
        if Tj == None:
            # Use ambient temperature
            self.Tj = self.T
        else:
            # Use input temperature
            self.Tj = Tj

        # Compute the gas/liquid equilibrium
        m_eq, xi, K = self.oil_mixture.equilibrium(self.m_mixture, self.Tj,
                                                   self.P)

        # Compute the gas phase properties
        if np.sum(m_eq[0,:]) == 0:
            self.gas = None
            self.m_gas = m_eq[0,:]
            self.rho_gas = None
            self.mu_gas = None
            self.sigma_gas = None
        else:
            self.gas = dbm.FluidParticle(self.oil_mixture.composition,
                                         fp_type=0,
                                         delta=oil_mixture.delta,
                                         user_data=oil_mixture.user_data)
            self.m_gas = m_eq[0,:]
            self.rho_gas = self.gas.density(self.m_gas, self.Tj, self.P)
            self.mu_gas = self.gas.viscosity(self.m_gas, self.Tj, self.P)
            self.sigma_gas = self.gas.interface_tension(self.m_gas, self.Tj,
                                                        self.S, self.P)

        # Compute the liquid phase properties
        if np.sum(m_eq[1,:]) == 0:
            self.oil = None
            self.m_oil = m_eq[1,:]
            self.rho_oil = None
            self.mu_oil = None
            self.sigma_oil = None
        else:
            self.oil = dbm.FluidParticle(self.oil_mixture.composition,
                                         fp_type=1,
                                         delta=oil_mixture.delta,
                                         user_data=oil_mixture.user_data)

            self.m_oil = m_eq[1,:]
            self.rho_oil = self.oil.density(self.m_oil, self.Tj, self.P)
            self.mu_oil = self.oil.viscosity(self.m_oil, self.Tj, self.P)
            self.sigma_oil = self.oil.interface_tension(self.m_oil, self.Tj,
                                                    self.S, self.P)
Beispiel #6
0
def get_sim_data():
    """
    Create the data needed to initialize a simulation
    
    Performs the steps necessary to set up a stratified plume model simulation
    and passes the input variables to the `Model` object and 
    `Model.simulate()` method.  
    
    Returns
    -------
    profile : `ambient.Profile` object
        Return a profile object from the BM54 CTD data
    particles : list of `PlumeParticle` objects
        List of `PlumeParticle` objects containing the dispersed phase initial
        conditions
    z : float
        Depth of the release port (m)
    R : float
        Radius of the release port (m)
    maxit : float
        Maximum number of iterations to converge between inner and outer
        plumes
    toler : float
        Relative error tolerance to accept for convergence (--)
    delta_z : float
        Maximum step size to use in the simulation (m).  The ODE solver 
        in `calculate` is set up with adaptive step size integration, so 
        in theory this value determines the largest step size in the 
        output data, but not the numerical stability of the calculation.
    
    """
    # Get the ambient CTD data
    profile = get_profile()

    # Specify the release location and geometry and initialize a particle
    # list
    z0 = 300.
    R = 0.15
    particles = []

    # Add a dissolving particle to the list
    composition = ['oxygen', 'nitrogen', 'argon']
    yk = np.array([1.0, 0., 0.])
    o2 = dbm.FluidParticle(composition)
    Q_N = 150. / 60. / 60.
    de = 0.005
    lambda_1 = 0.85
    particles.append(
        stratified_plume_model.particle_from_Q(profile, z0, o2, yk, Q_N, de,
                                               lambda_1))

    # Add an insoluble particle to the list
    composition = ['inert']
    yk = np.array([1.])
    oil = dbm.InsolubleParticle(True, True)
    mb0 = 50.
    de = 0.01
    lambda_1 = 0.8
    particles.append(
        stratified_plume_model.particle_from_mb0(profile, z0, oil, [1.], mb0,
                                                 de, lambda_1))

    # Set the other simulation parameters
    maxit = 2
    toler = 0.2
    delta_z = 1.0

    # Return the results
    return (profile, particles, z0, R, maxit, toler, delta_z)
Beispiel #7
0
def test_particle_obj():
    """
    Test the object behavior for the `PlumeParticle` object
    
    Test the instantiation and attribute data for the `PlumeParticle` object 
    of the `stratified_plume_model` module.
    
    """
    # Set up the base parameters describing a particle object
    T = 273.15 + 15.
    P = 150e5
    Sa = 35.
    Ta = 273.15 + 4.
    composition = ['methane', 'ethane', 'propane', 'oxygen']
    yk = np.array([0.85, 0.07, 0.08, 0.0])
    de = 0.005
    lambda_1 = 0.85
    K = 1.
    Kt = 1.
    fdis = 1e-6

    # Compute a few derived quantities
    bub = dbm.FluidParticle(composition)
    nb0 = 1.e5
    m0 = bub.masses_by_diameter(de, T, P, yk)

    # Create a `PlumeParticle` object
    bub_obj = dispersed_phases.PlumeParticle(bub, m0, T, nb0, lambda_1, P, Sa,
                                             Ta, K, Kt, fdis)

    # Check if the initialized attributes are correct
    for i in range(len(composition)):
        assert bub_obj.composition[i] == composition[i]
    assert_array_almost_equal(bub_obj.m0, m0, decimal=6)
    assert bub_obj.T0 == T
    assert_array_almost_equal(bub_obj.m, m0, decimal=6)
    assert bub_obj.T == T
    assert bub_obj.cp == seawater.cp() * 0.5
    assert bub_obj.K == K
    assert bub_obj.K_T == Kt
    assert bub_obj.fdis == fdis
    for i in range(len(composition) - 1):
        assert bub_obj.diss_indices[i] == True
    assert bub_obj.diss_indices[-1] == False
    assert bub_obj.nb0 == nb0
    assert bub_obj.lambda_1 == lambda_1

    # Including the values after the first call to the update method
    us_ans = bub.slip_velocity(m0, T, P, Sa, Ta)
    rho_p_ans = bub.density(m0, T, P)
    A_ans = bub.surface_area(m0, T, P, Sa, Ta)
    Cs_ans = bub.solubility(m0, T, P, Sa)
    beta_ans = bub.mass_transfer(m0, T, P, Sa, Ta)
    beta_T_ans = bub.heat_transfer(m0, T, P, Sa, Ta)
    assert bub_obj.us == us_ans
    assert bub_obj.rho_p == rho_p_ans
    assert bub_obj.A == A_ans
    assert_array_almost_equal(bub_obj.Cs, Cs_ans, decimal=6)
    assert_array_almost_equal(bub_obj.beta, beta_ans, decimal=6)
    assert bub_obj.beta_T == beta_T_ans

    # No need to test the properties or diameter objects since they are
    # inherited from the `single_bubble_model` and tested in `test_sbm`.

    # Check functionality of insoluble particle
    drop = dbm.InsolubleParticle(isfluid=True, iscompressible=True)
    m0 = drop.mass_by_diameter(de, T, P, Sa, Ta)
    drop_obj = dispersed_phases.PlumeParticle(drop,
                                              m0,
                                              T,
                                              nb0,
                                              lambda_1,
                                              P,
                                              Sa,
                                              Ta,
                                              K,
                                              fdis=fdis,
                                              K_T=Kt)
    assert len(drop_obj.composition) == 1
    assert drop_obj.composition[0] == 'inert'
    assert_array_almost_equal(drop_obj.m0, m0, decimal=6)
    assert drop_obj.T0 == T
    assert_array_almost_equal(drop_obj.m, m0, decimal=6)
    assert drop_obj.T == T
    assert drop_obj.cp == seawater.cp() * 0.5
    assert drop_obj.K == K
    assert drop_obj.K_T == Kt
    assert drop_obj.fdis == fdis
    assert drop_obj.diss_indices[0] == True
    assert drop_obj.nb0 == nb0
    assert drop_obj.lambda_1 == lambda_1

    # Including the values after the first call to the update method
    us_ans = drop.slip_velocity(m0, T, P, Sa, Ta)
    rho_p_ans = drop.density(T, P, Sa, Ta)
    A_ans = drop.surface_area(m0, T, P, Sa, Ta)
    beta_T_ans = drop.heat_transfer(m0, T, P, Sa, Ta)
    assert drop_obj.us == us_ans
    assert drop_obj.rho_p == rho_p_ans
    assert drop_obj.A == A_ans
    assert drop_obj.beta_T == beta_T_ans
Beispiel #8
0
def get_sim_data():
    """
    Create the data needed to initialize a simulation

    Performs the steps necessary to set up a bent plume model simulation
    and passes the input variables to the `Model` object and
    `Model.simulate()` method.

    Returns
    -------
    profile : `ambient.Profile` object
        Return a profile object from the BM54 CTD data
    z0 : float
        Depth of the release port (m)
    D : float
        Diameter of the release port (m)
    Vj : float
        Initial velocity of the jet (m/s)
    phi_0 : float
        Vertical angle from the horizontal for the discharge orientation
        (rad in range +/- pi/2)
    theta_0 : float
        Horizontal angle from the x-axis for the discharge orientation.
        The x-axis is taken in the direction of the ambient current.
        (rad in range 0 to 2 pi)
    Sj : float
        Salinity of the continuous phase fluid in the discharge (psu)
    Tj : float
        Temperature of the continuous phase fluid in the discharge (T)
    cj : ndarray
        Concentration of passive tracers in the discharge (user-defined)
    tracers : string list
        List of passive tracers in the discharge.  These can be chemicals
        present in the ambient `profile` data, and if so, entrainment of
        these chemicals will change the concentrations computed for these
        tracers.  However, none of these concentrations are used in the
        dissolution of the dispersed phase.  Hence, `tracers` should not
        contain any chemicals present in the dispersed phase particles.
    particles : list of `Particle` objects
        List of `Particle` objects describing each dispersed phase in the
        simulation
    dt_max : float
        Maximum step size to take in the storage of the simulation
        solution (s)
    sd_max : float
        Maximum number of orifice diameters to compute the solution along
        the plume centerline (m/m)

    """
    # Get the ambient CTD data
    profile = get_profile()

    # Specify the release location and geometry and initialize a particle
    # list
    z0 = 300.
    D = 0.3
    particles = []

    # Add a dissolving particle to the list
    composition = ['oxygen', 'nitrogen', 'argon']
    yk = np.array([1.0, 0., 0.])
    o2 = dbm.FluidParticle(composition)
    Q_N = 1.5 / 60. / 60.
    de = 0.009
    lambda_1 = 0.85
    (m0, T0, nb0, P, Sa, Ta) = dispersed_phases.initial_conditions(
        profile, z0, o2, yk, Q_N, 1, de)
    particles.append(bent_plume_model.Particle(0., 0., z0, o2, m0, T0,
        nb0, lambda_1, P, Sa, Ta, K=1., K_T=1., fdis=1.e-6, t_hyd=0.))

    # Add an insoluble particle to the list
    composition = ['inert']
    yk = np.array([1.])
    oil = dbm.InsolubleParticle(True, True)
    mb0 = 1.
    de = 0.01
    lambda_1 = 0.8
    (m0, T0, nb0, P, Sa, Ta) = dispersed_phases.initial_conditions(
        profile, z0, oil, yk, mb0, 1, de)
    particles.append(bent_plume_model.Particle(0., 0., z0, oil, m0, T0,
        nb0, lambda_1, P, Sa, Ta, K=1., K_T=1., fdis=1.e-6, t_hyd=0.))

    # Set the other simulation parameters
    Vj = 0.
    phi_0 = -np.pi/2.
    theta_0 = 0.
    Sj = 0.
    Tj = Ta
    cj = np.array([1.])
    tracers = ['tracer']
    dt_max = 60.
    sd_max = 3000.

    # Return the results
    return (profile, np.array([0., 0., z0]), D, Vj, phi_0, theta_0, Sj, Tj,
        cj, tracers, particles, dt_max, sd_max)
Beispiel #9
0
    def _update(self):
        """
        Initialize bent_plume_model for simulation run

        Set up the ambient profile, initial conditions, and model parameters
        for a new simulation run of the `bent_plume_model`.

        """
        # Get an ambient Profile object
        self.profile = get_ambient_profile(self.water,
                                           self.current,
                                           ca=self.ca)

        # Import the oil with the desired gas-to-oil ratio
        if self.new_oil:
            self.oil, self.mass_flux = dbm_utilities.get_oil(
                self.substance, self.q_oil, self.gor, self.ca, self.q_type)
            self.new_oil = False

        # Find the ocean conditions at the release
        self.T0, self.S0, self.P0 = self.profile.get_values(
            self.z0, ['temperature', 'salinity', 'pressure'])

        # Define some of the constant initial conditions
        self.Sj = 0.
        self.Tj = self.T0
        self.cj = 1.
        self.tracers = ['tracer']

        # Compute the equilibrium mixture properties at the release
        m, xi, K = self.oil.equilibrium(self.mass_flux, self.Tj, self.P0)

        # Create the discrete bubble model objects for gas and liquid
        self.gas = dbm.FluidParticle(self.oil.composition,
                                     fp_type=0,
                                     delta=self.oil.delta,
                                     user_data=self.oil.user_data)
        self.liq = dbm.FluidParticle(self.oil.composition,
                                     fp_type=1,
                                     delta=self.oil.delta,
                                     user_data=self.oil.user_data)

        # Compute the bubble and droplet volume size distributions
        breakup_model = psm.Model(self.profile, self.oil, self.mass_flux,
                                  self.z0, self.Tj)
        breakup_model.simulate(self.d0,
                               model_gas='wang_etal',
                               model_oil='sintef')
        self.d_gas, self.vf_gas, self.d_liq, self.vf_liq = \
            breakup_model.get_distributions(self.num_gas_elements,
            self.num_oil_elements)

        # Create the `bent_plume_model` particle list
        self.disp_phases = []
        self.disp_phases += particles(np.sum(m[0, :]), self.d_gas, self.vf_gas,
                                      self.profile, self.gas, xi[0, :], 0., 0.,
                                      self.z0, self.Tj, 0.9, False)
        self.disp_phases += particles(np.sum(m[1, :]), self.d_liq, self.vf_liq,
                                      self.profile, self.liq, xi[1, :], 0., 0.,
                                      self.z0, self.Tj, 0.98, False)

        # Set some of the hidden model parameters
        # TODO:  consider which of these should be editable by the user
        self.track = True
        self.dt_max = 5. * 3600.
        self.sd_max = 3. * self.z0 / self.d0

        # Create the initialized `bent_plume_model` object
        self.bpm = bent_plume_model.Model(self.profile)

        # Set the flag to indicate the model is ready to run
        self.update = True
Beispiel #10
0
def test_simulation():
    """
    Test the output from the `Model.simulate` method
    
    Test the output of the `Model.simulate` method of the 
    `single_bubble_model` module.  These tests include a single component 
    bubble, multi-component bubbles and drops, and multi-component fluid
    particles with gas stripping from the ambient dissolved chemicals.
    
    """
    # Get the ambient profile data
    profile = get_profile()

    # Initialize a Model object
    sbm = single_bubble_model.Model(profile)

    # Set up the initial conditions
    composition = ['methane', 'ethane', 'propane', 'oxygen']
    bub = dbm.FluidParticle(composition)
    mol_frac = np.array([0.90, 0.07, 0.03, 0.0])
    de = 0.005
    z0 = 1000.
    T0 = 273.15 + 30.

    # Run the simulation
    sbm.simulate(bub,
                 np.array([0., 0., z0]),
                 de,
                 mol_frac,
                 T0,
                 K_T=1,
                 fdis=1e-8,
                 delta_t=10.)

    # Check the solution
    assert sbm.y.shape[0] == 1117
    assert sbm.y.shape[1] == 8
    assert sbm.t.shape[0] == 1117
    assert_approx_equal(sbm.t[-1], 11016.038751523512, significant=6)
    assert_array_almost_equal(sbm.y[-1, :],
                              np.array([
                                  0.00000000e+00, 0.00000000e+00,
                                  3.36934635e+02, 4.31711152e-14,
                                  6.66318106e-15, -2.91389824e-13,
                                  -1.08618680e-15, -1.37972400e-07
                              ]),
                              decimal=6)

    # Write the output files
    sbm.save_sim('./output/sbm_data.nc', './test_BM54.nc',
                 'Results of ./test_sbm.py script')

    sbm.save_txt('./output/sbm_data', './test_BM54.nc',
                 'Results of ./test_sbm.py script')

    # Reload the simulation
    sbm_f = single_bubble_model.Model(simfile='./output/sbm_data.nc')

    # Check that the attributes are loaded correctly
    assert sbm_f.y[0, 0] == sbm.y[0, 0]  # x0
    assert sbm_f.y[0, 1] == sbm.y[0, 1]  # y0
    assert sbm_f.y[0, 2] == sbm.y[0, 2]  # z0

    assert_array_almost_equal(sbm_f.particle.m0, sbm.particle.m0, decimal=6)
    assert sbm_f.particle.T0 == sbm.particle.T0
    print sbm_f.particle.K_T, sbm.particle.K_T
    assert sbm_f.particle.K == sbm.particle.K
    assert sbm_f.particle.K_T == sbm.particle.K_T
    assert sbm_f.particle.fdis == sbm.particle.fdis
    assert sbm_f.K_T0 == sbm.K_T0
    assert sbm_f.delta_t == sbm.delta_t
    (T, S, P) = profile.get_values(1000.,
                                   ['temperature', 'salinity', 'pressure'])
    (Tp, Sp,
     Pp) = sbm_f.profile.get_values(1000.,
                                    ['temperature', 'salinity', 'pressure'])
    assert Tp == T
    assert Sp == S
    assert Pp == P

    # Check that the results are still correct
    assert sbm.y.shape[0] == 1117
    assert sbm.y.shape[1] == 8
    assert sbm.t.shape[0] == 1117
    assert_approx_equal(sbm.t[-1], 11016.038751523512, significant=6)
    assert_array_almost_equal(sbm.y[-1, :],
                              np.array([
                                  0.00000000e+00, 0.00000000e+00,
                                  3.36934635e+02, 4.31711152e-14,
                                  6.66318106e-15, -2.91389824e-13,
                                  -1.08618680e-15, -1.37972400e-07
                              ]),
                              decimal=6)

    # Load the data in the txt file and check the solution
    data = np.loadtxt('./output/sbm_data.txt')
    assert sbm.y.shape[0] == 1117
    assert sbm.y.shape[1] == 8
    assert sbm.t.shape[0] == 1117
    assert_approx_equal(sbm.t[-1], 11016.038751523512, significant=6)
    assert_array_almost_equal(sbm.y[-1, :],
                              np.array([
                                  0.00000000e+00, 0.00000000e+00,
                                  3.36934635e+02, 4.31711152e-14,
                                  6.66318106e-15, -2.91389824e-13,
                                  -1.08618680e-15, -1.37972400e-07
                              ]),
                              decimal=6)

    # Create an inert particle that is compressible
    oil = dbm.InsolubleParticle(True, True, rho_p=840.)
    mol_frac = np.array([1.])

    # Specify the remaining particle initial conditions
    de = 0.03
    z0 = 1000.
    T0 = 273.15 + 30.

    # Simulate the trajectory through the water column and plot the results
    sbm.simulate(oil,
                 np.array([0., 0., z0]),
                 de,
                 mol_frac,
                 T0,
                 K_T=1,
                 delta_t=10.)
    ans = np.array([
        0.00000000e+00, 0.00000000e+00, 1.16067764e-01, 1.26136097e-02,
        7.57374681e+03
    ])
    for i in range(5):
        assert_approx_equal(sbm.y[-1, i], ans[i], significant=6)
Beispiel #11
0
def test_particle_obj():
    """
    Test the object behavior for the `Particle` object
    
    Test the instantiation and attribute data for the `Particle` object of 
    the `single_bubble_model` module.
    
    """
    # Set up the base parameters describing a particle object
    T = 273.15 + 15.
    P = 150e5
    Sa = 35.
    Ta = 273.15 + 4.
    composition = ['methane', 'ethane', 'propane', 'oxygen']
    yk = np.array([0.85, 0.07, 0.08, 0.0])
    de = 0.005
    K = 1.
    Kt = 1.
    fdis = 1e-6

    # Compute a few derived quantities
    bub = dbm.FluidParticle(composition)
    m0 = bub.masses_by_diameter(de, T, P, yk)

    # Create a `SingleParticle` object
    bub_obj = dispersed_phases.SingleParticle(bub, m0, T, K, fdis=fdis, K_T=Kt)

    # Check if the initial attributes are correct
    for i in range(len(composition)):
        assert bub_obj.composition[i] == composition[i]
    assert_array_almost_equal(bub_obj.m0, m0, decimal=6)
    assert bub_obj.T0 == T
    assert bub_obj.cp == seawater.cp() * 0.5
    assert bub_obj.K == K
    assert bub_obj.K_T == Kt
    assert bub_obj.fdis == fdis
    for i in range(len(composition) - 1):
        assert bub_obj.diss_indices[i] == True
    assert bub_obj.diss_indices[-1] == False

    # Check if the values returned by the `properties` method match the input
    (us, rho_p, A, Cs, beta, beta_T,
     T_ans) = bub_obj.properties(m0, T, P, Sa, Ta, 0.)
    us_ans = bub.slip_velocity(m0, T, P, Sa, Ta)
    rho_p_ans = bub.density(m0, T, P)
    A_ans = bub.surface_area(m0, T, P, Sa, Ta)
    Cs_ans = bub.solubility(m0, T, P, Sa)
    beta_ans = bub.mass_transfer(m0, T, P, Sa, Ta)
    beta_T_ans = bub.heat_transfer(m0, T, P, Sa, Ta)
    assert us == us_ans
    assert rho_p == rho_p_ans
    assert A == A_ans
    assert_array_almost_equal(Cs, Cs_ans, decimal=6)
    assert_array_almost_equal(beta, beta_ans, decimal=6)
    assert beta_T == beta_T_ans
    assert T == T_ans

    # Check that dissolution shuts down correctly
    m_dis = np.array([m0[0] * 1e-10, m0[1] * 1e-8, m0[2] * 1e-3, 1.5e-5])
    (us, rho_p, A, Cs, beta, beta_T,
     T_ans) = bub_obj.properties(m_dis, T, P, Sa, Ta, 0)
    assert beta[0] == 0.
    assert beta[1] == 0.
    assert beta[2] > 0.
    assert beta[3] > 0.
    m_dis = np.array([m0[0] * 1e-10, m0[1] * 1e-8, m0[2] * 1e-7, 1.5e-16])
    (us, rho_p, A, Cs, beta, beta_T,
     T_ans) = bub_obj.properties(m_dis, T, P, Sa, Ta, 0.)
    assert np.sum(beta[0:-1]) == 0.
    assert us == 0.
    assert rho_p == seawater.density(Ta, Sa, P)

    # Check that heat transfer shuts down correctly
    (us, rho_p, A, Cs, beta, beta_T,
     T_ans) = bub_obj.properties(m_dis, Ta, P, Sa, Ta, 0)
    assert beta_T == 0.
    (us, rho_p, A, Cs, beta, beta_T,
     T_ans) = bub_obj.properties(m_dis, T, P, Sa, Ta, 0)
    assert beta_T == 0.

    # Check the value returned by the `diameter` method
    de_p = bub_obj.diameter(m0, T, P, Sa, Ta)
    assert_approx_equal(de_p, de, significant=6)

    # Check functionality of insoluble particle
    drop = dbm.InsolubleParticle(isfluid=True, iscompressible=True)
    m0 = drop.mass_by_diameter(de, T, P, Sa, Ta)

    # Create a `Particle` object
    drop_obj = dispersed_phases.SingleParticle(drop,
                                               m0,
                                               T,
                                               K,
                                               fdis=fdis,
                                               K_T=Kt)

    # Check if the values returned by the `properties` method match the input
    (us, rho_p, A, Cs, beta, beta_T,
     T_ans) = drop_obj.properties(np.array([m0]), T, P, Sa, Ta, 0)
    us_ans = drop.slip_velocity(m0, T, P, Sa, Ta)
    rho_p_ans = drop.density(T, P, Sa, Ta)
    A_ans = drop.surface_area(m0, T, P, Sa, Ta)
    beta_T_ans = drop.heat_transfer(m0, T, P, Sa, Ta)
    assert us == us_ans
    assert rho_p == rho_p_ans
    assert A == A_ans
    assert beta_T == beta_T_ans

    # Check that heat transfer shuts down correctly
    (us, rho_p, A, Cs, beta, beta_T,
     T_ans) = drop_obj.properties(m_dis, Ta, P, Sa, Ta, 0)
    assert beta_T == 0.
    (us, rho_p, A, Cs, beta, beta_T,
     T_ans) = drop_obj.properties(m_dis, T, P, Sa, Ta, 0)
    assert beta_T == 0.

    # Check the value returned by the `diameter` method
    de_p = drop_obj.diameter(m0, T, P, Sa, Ta)
    assert_approx_equal(de_p, de, significant=6)
Beispiel #12
0
import numpy as np
import matplotlib.pyplot as plt

if __name__ == '__main__':

    # Define the composition of a natural gas
    composition = ['neohexane', 'benzene', 'toluene', 'ethylbenzene']
    mol_frac = np.array([0.25, 0.28, 0.18, 0.29])

    # Specify that we are interested in properties for the liquid phase
    fl_type = 1

    # Create a DBM FluidParticle object for this simple oil assuming zeros
    # for all the binary interaction coefficients
    delta = np.zeros((4, 4))
    oil = dbm.FluidParticle(composition, fl_type, delta)

    # Specify some generic deepwater ocean conditions
    P = 150.0 * 1.0e5
    Ta = 273.15 + 4.0
    Sa = 34.5

    # Echo the ambient conditions to the screen
    print '\nAmbient conditions: \n'
    print '   P = %g (Pa)' % P
    print '   T = %g (K)' % Ta
    print '   S = %g (psu)' % Sa
    print '   rho_sw = %g (kg/m^3)' % (seawater.density(Ta, Sa, P))

    # Get the general properties of the oil
    mf = oil.mass_frac(mol_frac)
Beispiel #13
0
import numpy as np

if __name__ == '__main__':

    # Open an ambient profile object from the netCDF dataset
    nc = '../../test/output/test_bm54.nc'
    bm54 = ambient.Profile(nc, chem_names='all')
    bm54.close_nc()

    # Initialize a single_bubble_model.Model object with this data
    sbm = single_bubble_model.Model(bm54)

    # Create a light oil droplet particle to track
    composition = ['benzene', 'toluene', 'ethylbenzene']
    drop = dbm.FluidParticle(composition, fp_type=1.)

    # Set the mole fractions of each component at release.
    mol_frac = np.array([0.4, 0.3, 0.3])

    # Specify the remaining particle initial conditions
    de = 0.02
    z0 = 1000.
    T0 = 273.15 + 30.

    # Simulate the trajectory through the water column and plot the results
    sbm.simulate(drop, z0, de, mol_frac, T0, K_T=1, fdis=1e-8, delta_t=10.)
    sbm.post_process()

    # Save the simulation to a netCDF file
    sbm.save_sim('./drop.nc', '../../test/output/test_bm54.nc',
Beispiel #14
0
    except RuntimeError:
        # Tell the user to create the dataset
        print('CTD data not available; run test cases in ./test first.')

    # Create the stratified plume model object
    spm = stratified_plume_model.Model(ctd)

    # Set the release conditions
    T0 = 273.15 + 35.  # Release temperature in K
    R = 0.15  # Radius of leak source in m

    # Create the gas phase particles
    composition = ['methane', 'ethane', 'propane', 'oxygen']
    yk = np.array([0.93, 0.05, 0.02, 0.0])
    gas = dbm.FluidParticle(composition)
    z0 = 1000.
    disp_phases = []

    # Larger free gas bubbles
    mb0 = 8.  # total mass flux in kg/s
    de = 0.025  # bubble diameter in m
    lambda_1 = 0.85
    disp_phases.append(
        stratified_plume_model.particle_from_mb0(ctd, z0, gas, yk, mb0, de,
                                                 lambda_1, T0))

    # Smaller free gas bubbles (note, it is not necessary to have more than
    # one bubble size)
    mb0 = 2.  # total mass flux in kg/s
    de = 0.0075  # bubble diameter in m
Beispiel #15
0
    def get_particles(self, composition, data, md_gas0, md_oil0, profile, d50_gas, d50_oil, nbins,
                  T0, z0, dispersant, sigma_fac, oil, mass_frac, hydrate, inert_drop):
        """
        docstring for get_particles

        """

        # Reduce surface tension if dispersant is applied
        if dispersant is True:
            sigma = np.array([[1.], [1.]]) * sigma_fac
        else:
            sigma = np.array([[1.], [1.]])

        # Create DBM objects for the bubbles and droplets
        bubl = dbm.FluidParticle(composition, fp_type=0, sigma_correction=sigma[0], user_data=data)
        drop = dbm.FluidParticle(composition, fp_type=1, sigma_correction=sigma[1], user_data=data)

        # Get the local ocean conditions
        T, S, P = profile.get_values(z0, ['temperature', 'salinity', 'pressure'])
        rho = seawater.density(T, S, P)

        # Get the mole fractions of the released fluids
        molf_gas = bubl.mol_frac(md_gas0)
        molf_oil = drop.mol_frac(md_oil0)
        print molf_gas
        print molf_oil

        # Use the Rosin-Rammler distribution to get the mass flux in each
        # size class
#        de_gas, md_gas = sintef.rosin_rammler(nbins, d50_gas, np.sum(md_gas0),
#                                              bubl.interface_tension(md_gas0, T0, S, P),
#                                              bubl.density(md_gas0, T0, P), rho)
#        de_oil, md_oil = sintef.rosin_rammler(nbins, d50_oil, np.sum(md_oil0),
#                                              drop.interface_tension(md_oil0, T0, S, P),
#                                              drop.density(md_oil0, T0, P), rho)

        # Get the user defined particle size distibution
        de_oil, vf_oil, de_gas, vf_gas = self.userdefined_de()
        md_gas = np.sum(md_gas0) * vf_gas
        md_oil = np.sum(md_oil0) * vf_oil

        # Define a inert particle to be used if inert liquid particles are use
        # in the simulations
        molf_inert = 1.
        isfluid = True
        iscompressible = True
        rho_o = drop.density(md_oil0, T0, P)
        inert = dbm.InsolubleParticle(isfluid, iscompressible, rho_p=rho_o, gamma=40.,
                                      beta=0.0007, co=2.90075e-9)

        # Create the particle objects
        particles = []
        t_hyd = 0.

        # Bubbles
        for i in range(nbins):
            if md_gas[i] > 0.:
                (m0, T0, nb0, P, Sa, Ta) = dispersed_phases.initial_conditions(
                            profile, z0, bubl, molf_gas, md_gas[i], 2, de_gas[i], T0)
                # Get the hydrate formation time for bubbles
                if hydrate is True and dispersant is False:
                    t_hyd = dispersed_phases.hydrate_formation_time(bubl, z0, m0, T0, profile)
                    if np.isinf(t_hyd):
                        t_hyd = 0.
                else:
                    t_hyd = 0.
                particles.append(bpm.Particle(0., 0., z0, bubl, m0, T0, nb0,
                                              1.0, P, Sa, Ta, K=1., K_T=1., fdis=1.e-6, t_hyd=t_hyd))

        # Droplets
        for i in range(len(de_oil)):
            # Add the live droplets to the particle list
            if md_oil[i] > 0. and not inert_drop:
                (m0, T0, nb0, P, Sa, Ta) = dispersed_phases.initial_conditions(
                        profile, z0, drop, molf_oil, md_oil[i], 2, de_oil[i], T0)
                # Get the hydrate formation time for bubbles
                if hydrate is True and dispersant is False:
                    t_hyd = dispersed_phases.hydrate_formation_time(drop, z0, m0, T0, profile)
                    if np.isinf(t_hyd):
                            t_hyd = 0.
                else:
                    t_hyd = 0.
                particles.append(bpm.Particle(0., 0., z0, drop, m0, T0, nb0,
                                                1.0, P, Sa, Ta, K=1., K_T=1., fdis=1.e-6, t_hyd=t_hyd))
            # Add the inert droplets to the particle list
            if md_oil[i] > 0. and inert_drop is True:
                (m0, T0, nb0, P, Sa, Ta) = dispersed_phases.initial_conditions(
                        profile, z0, inert, molf_oil, md_oil[i], 2, de_oil[i], T0)
                particles.append(bpm.Particle(0., 0., z0, inert, m0, T0, nb0,
                        1.0, P, Sa, Ta, K=1., K_T=1., fdis=1.e-6, t_hyd=0.))

        # Define the lambda for particles
        model = params.Scales(profile, particles)
        for j in range(len(particles)):
            particles[j].lambda_1 = model.lambda_1(z0, j)

        # Return the particle list
        return particles
Beispiel #16
0
def get_sim_data():
    """
    Get the input data to the `params.Scales` object
    
    Create an `ambient.Profile` object and a list of 
    `stratified_plume_model.Particle` objects as the required input for 
    the `params.Scales` object.  
    
    Returns
    -------
    profile : `ambient.Profile` object
        profile object from the BM54 CTD data
    disp_phases: list
        list of `stratified_plume_model.Particle` objects describing the 
        blowout dispersed phases.
    z0 : float
        depth at the plume model origin (m)
    
    """
    # Get the netCDF file
    nc = test_sbm.make_ctd_file()

    # Create a profile object with all available chemicals in the CTD data
    profile = ambient.Profile(nc, chem_names='all')

    # Create the stratified plume model object
    spm = stratified_plume_model.Model(profile)

    # Set the release conditions
    T0 = 273.15 + 35.  # Release temperature in K
    R = 0.15  # Radius of leak source in m

    # Create the gas phase particles
    composition = ['methane', 'ethane', 'propane', 'oxygen']
    yk = np.array([0.93, 0.05, 0.02, 0.0])
    gas = dbm.FluidParticle(composition)
    z0 = 1000.
    disp_phases = []

    # Larger free gas bubbles
    mb0 = 8.  # total mass flux in kg/s
    de = 0.025  # bubble diameter in m
    lambda_1 = 0.85
    disp_phases.append(
        stratified_plume_model.particle_from_mb0(profile, z0, gas, yk, mb0, de,
                                                 lambda_1, T0))

    # Smaller free gas bubbles (note, it is not necessary to have more than
    # one bubble size)
    mb0 = 2.  # total mass flux in kg/s
    de = 0.0075  # bubble diameter in m
    lambda_1 = 0.9
    disp_phases.append(
        stratified_plume_model.particle_from_mb0(profile, z0, gas, yk, mb0, de,
                                                 lambda_1, T0))

    # Liquid hydrocarbon.  This could either be a dissolving phase (mixture
    # of liquid phases) or an inert phase.  We demonstrate here the simple
    # case of an inert oil phase
    oil = dbm.InsolubleParticle(True,
                                True,
                                rho_p=890.,
                                gamma=30.,
                                beta=0.0007,
                                co=2.90075e-9)
    mb0 = 10.  # total mass flux in kg/s
    de = 0.004  # bubble diameter in m
    lambda_1 = 0.9
    disp_phases.append(
        stratified_plume_model.particle_from_mb0(profile, z0, oil,
                                                 np.array([1.]), mb0, de,
                                                 lambda_1, T0))

    # Return the simulation data
    return (profile, disp_phases, z0)
Beispiel #17
0
    nc = '../../test/output/lake.nc'
    try:
        # Open the lake dataset as a Profile object if it exists
        lake = ambient.Profile(nc, chem_names=['oxygen', 'nitrogen', 'argon'])

    except RuntimeError:
        # Create the lake netCDF dataset and get the Profile object
        lake = get_lake_data()

    # Create the stratified plume model object
    spm = stratified_plume_model.Model(lake)

    # Create the dispersed phase particles
    composition = ['oxygen', 'nitrogen', 'argon']
    yk = np.array([1.0, 0., 0.])
    o2 = dbm.FluidParticle(composition, isair=True)
    z0 = 46.
    bubbles = []

    # Small bubble
    Q_N = 30. / 60. / 60.
    de = 0.001
    lambda_1 = 0.9
    bubbles.append(
        stratified_plume_model.particle_from_Q(lake,
                                               z0,
                                               o2,
                                               yk,
                                               Q_N,
                                               de,
                                               lambda_1,