Exemplo n.º 1
0
def get_profile(profile_fname, z0, D, uj, vj, wj, Tj, Sj, ua, T, F, 
                sign_dp, H):
    """
    Create and ambient profile dataset for an integral jet model simulation
    
    """
    # Use mks units
    g = 9.81
    rho_w = 1000. 
    Pa = 101325.
    
    # Get the ambient density at the discharge from F (Assume water is 
    # incompressible for these laboratory experiments)
    rho_j = seawater.density(Tj, Sj, 101325. + rho_w * g * z0)
    Vj = np.sqrt(uj**2 + vj**2 + wj**2)
    if F == 0.:
        rho_a = rho_j
    else:
        rho_a = rho_j / (1. - Vj**2 / (sign_dp * F**2 * D * g))
    
    # Get the ambient stratification at the discharge from T
    if T == 0.:
        dpdz = 0.
    else:
        dpdz = sign_dp * (rho_a - rho_j) / (T * D)
    
    # Find the salinity at the discharge assuming the discharge temperature
    # matches the ambient temperature
    Ta = Tj
    def residual(Sa, rho, H):
        """
        docstring for residual
        
        """
        return rho - seawater.density(Ta, Sa, Pa + rho_w * g * H)
    Sa = fsolve(residual, 0., args=(rho_a, z0))
    
    # Find the salinity at the top and bottom assuming linear stratification
    if dpdz == 0.:
        S0 = Sa
        SH = Sa
    else:
        rho_H = dpdz * (H - z0) + rho_a
        rho_0 = dpdz * (0.- z0) + rho_a
        # Use potential density to get the salinity
        SH = fsolve(residual, Sa, args=(rho_H, z0))
        S0 = fsolve(residual, Sa, args=(rho_0, z0))
    
    # Build the ambient data arrays    
    z = np.array([0., H])
    T = np.array([Ta, Ta])
    S = np.array([S0, SH])
    ua = np.array([ua, ua])
    
    # Build the profile
    profile = build_profile(profile_fname, z, T, S, ua)
    
    # Return the ambient data
    return profile
Exemplo n.º 2
0
    def __init__(self, profile):
        super(ModelParams, self).__init__()

        # Store a reference density for the water column
        z_ave = profile.z_max - (profile.z_max - profile.z_min) / 2.
        T, S, P = profile.get_values(z_ave,
                                     ['temperature', 'salinity', 'pressure'])
        self.rho_r = seawater.density(T, S, P)

        # Store some physical constants
        self.g = 9.81
        self.Ru = 8.314510
Exemplo n.º 3
0
 def __init__(self, profile):
     super(ModelParams, self).__init__()
     
     # Store a reference density for the water column
     z_ave = profile.z_max - (profile.z_max - profile.z_min) / 2.
     T, S, P = profile.get_values(z_ave, ['temperature', 'salinity', 
                                  'pressure'])
     self.rho_r = seawater.density(T, S, P)
     
     # Store some physical constants
     self.g = 9.81
     self.Ru = 8.314510  
Exemplo n.º 4
0
def outer_surf(yi, p):
    """
    Compute the initial condition for the outer plume at the sea surface
    
    Computes the initial conditions for the first outer plume segment after
    the inner plume impinges on the free surface of the water body.  It is 
    assumed that the inner plume had significant volume flux and that this 
    first outer plume segment will be viable.
    
    Parameters
    ----------
    yi : `stratified_plume_model.InnerPlume` object
        Object for manipulating the inner plume state space
    p : `ModelParams` object
        Object containing the fixed model parameters for the stratified 
        plume model.
    
    Returns
    -------
    z0 : float
        Initial depth of the outer plume segment (m).
    y0 : ndarray
        Initial dependent variables state space for the outer plume segment.
    
    """
    # The outer plume is a mixture of inner plume fluid and ambient fluid
    # entrained from the water surface
    Q = (1. + p.fe) * yi.Q
    T = (yi.T + yi.Ta * p.fe) * yi.Q / Q
    s = (yi.s + yi.Sa * p.fe) * yi.Q / Q
    c = (yi.c + yi.ca * p.fe) * yi.Q / Q
    rho = seawater.density(T, s, yi.P)
    
    # Use a Froude number approach to set the initial width and velocity
    u = outer_fr(yi.u, Q, yi.b, yi.rho_a, rho, p.g, p.Fro_0)
    
    # Calculate the outer plume state space variables
    y0 = []
    Q = -Q
    y0.append(Q)
    y0.append(Q * (-u))
    y0.append(s * Q)
    y0.append(p.rho_r * seawater.cp() * T * Q)
    y0.extend(c * Q)
    
    # Return the outer plume initial condition
    return (yi.z, np.array(y0))
Exemplo n.º 5
0
def outer_surf(yi, p):
    """
    Compute the initial condition for the outer plume at the sea surface
    
    Computes the initial conditions for the first outer plume segment after
    the inner plume impinges on the free surface of the water body.  It is 
    assumed that the inner plume had significant volume flux and that this 
    first outer plume segment will be viable.
    
    Parameters
    ----------
    yi : `stratified_plume_model.InnerPlume` object
        Object for manipulating the inner plume state space
    p : `ModelParams` object
        Object containing the fixed model parameters for the stratified 
        plume model.
    
    Returns
    -------
    z0 : float
        Initial depth of the outer plume segment (m).
    y0 : ndarray
        Initial dependent variables state space for the outer plume segment.
    
    """
    # The outer plume is a mixture of inner plume fluid and ambient fluid
    # entrained from the water surface
    Q = (1. + p.fe) * yi.Q
    T = (yi.T + yi.Ta * p.fe) * yi.Q / Q
    s = (yi.s + yi.Sa * p.fe) * yi.Q / Q
    c = (yi.c + yi.ca * p.fe) * yi.Q / Q
    rho = seawater.density(T, s, yi.P)

    # Use a Froude number approach to set the initial width and velocity
    u = outer_fr(yi.u, Q, yi.b, yi.rho_a, rho, p.g, p.Fro_0)

    # Calculate the outer plume state space variables
    y0 = []
    Q = -Q
    y0.append(Q)
    y0.append(Q * (-u))
    y0.append(s * Q)
    y0.append(p.rho_r * seawater.cp() * T * Q)
    y0.extend(c * Q)

    # Return the outer plume initial condition
    return (yi.z, np.array(y0))
Exemplo n.º 6
0
def create_ambient_profile(data,
                           labels,
                           units,
                           comments,
                           nc_name,
                           summary,
                           source,
                           sea_name,
                           p_lat,
                           p_lon,
                           p_time,
                           ca=[]):
    """
    Create an ambient Profile object from given data

    Create an ambient.Profile object using the given CTD and current data.
    This function performs some standard operations to this data (unit
    conversion, computation of pressure, insertion of concentrations for
    dissolved gases, etc.) and returns the working ambient.Profile object.
    The idea behind this function is to separate data manipulation and
    creation of the ambient.Profile object from fetching of the data itself.

    Parameters
    ----------
    data : np.array
        Array of the ambient ocean data to write to the CTD file.  The
        contents and dimensions of this data are specified in the labels
        and units lists, below.
    labels : list
        List of string names of each variable in the data array.
    units : list
        List of units as strings for each variable in the data array.
    comments : list
        List of comments as strings that explain the types of data in the
        data array.  Typical comments include 'measured', 'modeled', or
        'computed'.
    nc_name : str
        String containing the file path and file name to use when creating
        the netCDF4 dataset that will contain this data.
    summary : str
        String describing the simulation for which this data will be used.
    source : str
        String documenting the source of the ambient ocean data provided.
    sea_name : str
        NC-compliant name for the ocean water body as a string.
    p_lat : float
        Latitude (deg)
    p_lon : float
        Longitude, negative is west of 0 (deg)
    p_time : netCDF4 time format
        Date and time of the CTD data using netCDF4.date2num().
    ca : list, default=[]
        List of gases for which to compute a standard dissolved gas profile;
        choices are 'nitrogen', 'oxygen', 'argon', and 'carbon_dioxide'.

    Returns
    -------
    profile : ambient.Profile
        Returns an ambient.Profile object for manipulating ambient water
        column data in TAMOC.

    """
    # Convert the data to standard units
    data, units = ambient.convert_units(data, units)

    # Create an empty netCDF4-classic datast to store this CTD data
    nc = ambient.create_nc_db(nc_name, summary, source, sea_name, p_lat, p_lon,
                              p_time)

    # Put the CTD and current profile data into the ambient netCDF file
    nc = ambient.fill_nc_db(nc, data, labels, units, comments, 0)

    # Compute and insert the pressure data
    z = nc.variables['z'][:]
    T = nc.variables['temperature'][:]
    S = nc.variables['salinity'][:]
    P = ambient.compute_pressure(z, T, S, 0)
    P_data = np.vstack((z, P)).transpose()
    nc = ambient.fill_nc_db(nc, P_data, ['z', 'pressure'], ['m', 'Pa'],
                            ['measured', 'computed'], 0)

    # Use this netCDF file to create an ambient object
    profile = ambient.Profile(
        nc, ztsp=['z', 'temperature', 'salinity', 'pressure', 'ua', 'va'])

    # Compute dissolved gas profiles to add to this dataset
    if len(ca) > 0:

        # Create a gas mixture object for air
        gases = ['nitrogen', 'oxygen', 'argon', 'carbon_dioxide']
        air = dbm.FluidMixture(gases)
        yk = np.array([0.78084, 0.20946, 0.009340, 0.00036])
        m = air.masses(yk)

        # Set atmospheric conditions
        Pa = 101325.

        # Compute the desired concentrations
        for i in range(len(ca)):

            # Initialize a dataset of concentration data
            conc = np.zeros(len(profile.z))

            # Compute the concentrations at each depth
            for j in range(len(conc)):

                # Get the local water column properties
                T, S, P = profile.get_values(
                    profile.z[j], ['temperature', 'salinity', 'pressure'])

                # Compute the gas solubility at this temperature and salinity
                # at the sea surface
                Cs = air.solubility(m, T, Pa, S)[0, :]

                # Adjust the solubility to the present depth
                Cs = Cs * seawater.density(T, S, P) / \
                    seawater.density(T, S, 101325.)

                # Extract the right chemical
                conc[j] = Cs[gases.index(ca[i])]

            # Add this computed dissolved gas to the Profile dataset
            data = np.vstack((profile.z, conc)).transpose()
            symbols = ['z', ca[i]]
            units = ['m', 'kg/m^3']
            comments = ['measured', 'computed from CTD data']
            profile.append(data, symbols, units, comments, 0)

    # Close the netCDF dataset
    profile.close_nc()

    # Return the profile object
    return profile
Exemplo n.º 7
0
 def get_variables(self, z0, u_inf):
     """
     Compute the governing variables at a given depth
     
     Compute the governing variables B (kinematic buoyancy flux), N 
     (buoyancy frequency) and u_slip (dispersed-phase slip velocity) at 
     the given depth and cross-flow velocity.  These are the main 
     ingredients to each of the scales calculations.
     
     Parameters
     ----------
     z0 : float
         Depth to evaluate the governing variables (m)
     u_inf : float
         Magnitude of the local ambient cross-flow velocity (m/s)
     
     Returns
     -------
     A tuble containing the governing variables:
         B : float
             Total kinematic buoyancy flux of all dispersed phases 
             together (m^4/s^3)
         N : float
             Local value of the ambient buoyancy frequency (1/s)
         u_slip : float
             Slip velocity of the dispersed phase containing the greatest
             buoyancy flux (m/s)
         u_inf : float
             Magnitude of the local ambient cross-flow velocity (m/s)
             TODO (S. Socolofsky, October 2013): Eventually, this should
             be read from the ambient CTD data and removed as an input to
             this method.
     
     Notes
     -----
     When more than one dispersed phase particle is present, the slip 
     velocity used as the governing variables is the value for the 
     dispersed phase particle that has the greatest effect on the dynamics
     of the plume.  This particle is the one for which the buoyancy flux
     is highest.  The governing variables, B, on the other hand is the 
     total buoyancy flux of all dispersed phase particles combined.  This 
     is consistent with the way the governing parameters have been used 
     in papers by Socolofsky and Adams (e.g., Socolofsky et al. 2011).
     
     """
     # Get the ambient data from the CTD profile
     Ta, Sa, P = self.profile.get_values(z0, ['temperature', 'salinity',
                                         'pressure'])
     rho = seawater.density(Ta, Sa, P)
     
     # Compute the properties of each dispersed-phase particle
     us = np.zeros(len(self.particles))
     rho_p = np.zeros(len(self.particles))
     m_p = np.zeros(len(self.particles))
     B_p = np.zeros(len(self.particles))
     for i in range(len(self.particles)):
         m0 = self.particles[i].m0
         T0 = self.particles[i].T0
         m_p[i] = np.sum(m0) * self.particles[i].nb0
         if m_p[i] > 0.:
             # Particles exist, get properties.  Make sure the algorithm 
             # uses the dirty bubble properties since this is supposed
             # to be the rise velocity averaged over the whole plume.
             us[i], rho_p[i]= self.particles[i].properties(m0, T0, P, Sa, 
                              Ta, np.inf)[0:2]
             B_p[i] = (rho - rho_p[i]) / rho * 9.81 * (m_p[i] / rho_p[i])
         else:
             # Particles dissolved, set to ambient conditions
             us[i] = 0.
             rho_p[i] = rho
             B_p[i] = 0.
     
     # Select the correct slip velocity
     u_slip = us[0]
     for i in range(len(self.particles) - 1):
         if B_p[i+1] > B_p[i]:
             u_slip = us[i+1]
     
     # Compute the total buoyancy flux
     B = np.sum(B_p)
     
     # Get the ambient buoyancy frequency
     N = self.profile.buoyancy_frequency(z0)
     
     # Return the governing parameters
     return (B, N, u_slip, u_inf)
Exemplo n.º 8
0
    # Simulate an experiment from Brandvik et al. (2013).  Their data uses
    # Oseberg oil, with the following reported properties
    rho_oil = 839.3
    mu_oil = 5.e-3
    sigma = 15.5e-3

    # We will simulate data from Table 3 in the Brandvik et al. (2013) paper.
    # These experiments have a nozzle diameter of 1.5 mm
    d0 = 0.0015

    # They also used seawater (assumed salinity of 34.5 psu) and released the
    # oil from a depth of about 6 m at a temperature of 13 deg C
    T = 273.15 + 13.
    S = 34.5
    rho = seawater.density(T, S, 101325.)
    P = 101325. + rho * 9.81 * 6.
    rho = seawater.density(T, S, P)
    mu = seawater.mu(T, S, P)

    # With this information, we can initialize a
    # `particle_size_models.PureJet` object
    jet = particle_size_models.PureJet(rho_oil,
                                       mu_oil,
                                       sigma,
                                       rho,
                                       mu,
                                       fp_type=1)

    # Brandvik et al. (2013) report the exit velocity at the nozzle.  We
    # need to convert this to a mass flow rate.  The mass flow rate should
Exemplo n.º 9
0
 def residual(Sa, rho, H):
     """
     docstring for residual
     
     """
     return rho - seawater.density(Ta, Sa, Pa + rho_w * g * H)
Exemplo n.º 10
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)
Exemplo n.º 11
0
    def get_variables(self, z0, u_inf):
        """
        Compute the governing variables at a given depth
        
        Compute the governing variables B (kinematic buoyancy flux), N 
        (buoyancy frequency) and u_slip (dispersed-phase slip velocity) at 
        the given depth and cross-flow velocity.  These are the main 
        ingredients to each of the scales calculations.
        
        Parameters
        ----------
        z0 : float
            Depth to evaluate the governing variables (m)
        u_inf : float
            Magnitude of the local ambient cross-flow velocity (m/s)
        
        Returns
        -------
        A tuble containing the governing variables:
            B : float
                Total kinematic buoyancy flux of all dispersed phases 
                together (m^4/s^3)
            N : float
                Local value of the ambient buoyancy frequency (1/s)
            u_slip : float
                Slip velocity of the dispersed phase containing the greatest
                buoyancy flux (m/s)
            u_inf : float
                Magnitude of the local ambient cross-flow velocity (m/s)
                TODO (S. Socolofsky, October 2013): Eventually, this should
                be read from the ambient CTD data and removed as an input to
                this method.
        
        Notes
        -----
        When more than one dispersed phase particle is present, the slip 
        velocity used as the governing variables is the value for the 
        dispersed phase particle that has the greatest effect on the dynamics
        of the plume.  This particle is the one for which the buoyancy flux
        is highest.  The governing variables, B, on the other hand is the 
        total buoyancy flux of all dispersed phase particles combined.  This 
        is consistent with the way the governing parameters have been used 
        in papers by Socolofsky and Adams (e.g., Socolofsky et al. 2011).
        
        """
        # Get the ambient data from the CTD profile
        Ta, Sa, P = self.profile.get_values(
            z0, ['temperature', 'salinity', 'pressure'])
        rho = seawater.density(Ta, Sa, P)

        # Compute the properties of each dispersed-phase particle
        us = np.zeros(len(self.particles))
        rho_p = np.zeros(len(self.particles))
        m_p = np.zeros(len(self.particles))
        B_p = np.zeros(len(self.particles))
        for i in range(len(self.particles)):
            m0 = self.particles[i].m0
            T0 = self.particles[i].T0
            m_p[i] = np.sum(m0) * self.particles[i].nb0
            if m_p[i] > 0.:
                # Particles exist, get properties.  Make sure the algorithm
                # uses the dirty bubble properties since this is supposed
                # to be the rise velocity averaged over the whole plume.
                us[i], rho_p[i] = self.particles[i].properties(
                    m0, T0, P, Sa, Ta, np.inf)[0:2]
                B_p[i] = (rho - rho_p[i]) / rho * 9.81 * (m_p[i] / rho_p[i])
            else:
                # Particles dissolved, set to ambient conditions
                us[i] = 0.
                rho_p[i] = rho
                B_p[i] = 0.

        # Select the correct slip velocity
        u_slip = us[0]
        for i in range(len(self.particles) - 1):
            if B_p[i + 1] > B_p[i]:
                u_slip = us[i + 1]

        # Compute the total buoyancy flux
        B = np.sum(B_p)

        # Get the ambient buoyancy frequency
        N = self.profile.buoyancy_frequency(z0)

        # Return the governing parameters
        return (B, N, u_slip, u_inf)
Exemplo n.º 12
0
    ds = ambient.Profile(nc)
    
    # Close the netCDF dataset
    ds.nc.close()
    
    # Since the netCDF file is now fully stored on the hard drive in the 
    # correct format, we can initialize an ambient.Profile object directly
    # from the netCDF file
    ds = ambient.Profile(nc_file, chem_names='all')
    
    # Plot the density profile using the interpolation function
    z = np.linspace(ds.nc.variables['z'].valid_min, 
                    ds.nc.variables['z'].valid_max, 250)
    rho = np.zeros(z.shape)
    tsp = ds.get_values(z, ['temperature', 'salinity', 'pressure'])
    for i in range(len(z)):
        rho[i] = seawater.density(tsp[i,0], tsp[i,1], tsp[i,2])
    
    fig = plt.figure()
    ax1 = plt.subplot(121)
    ax1.plot(rho, z)
    ax1.set_xlabel('Density (kg/m^3)')
    ax1.set_ylabel('Depth (m)')
    ax1.invert_yaxis()
    ax1.set_title('Computed data')
    plt.show()
    
    # Close the netCDF dataset
    ds.nc.close()

Exemplo n.º 13
0
    # Get the ambient.Profile object with the original CTD data
    profile = get_ctd_profile()

    # Compute a dissolved nitrogen profile...start with a model for air
    air = dbm.FluidMixture(['nitrogen', 'oxygen', 'argon', 'carbon_dioxide'])
    yk = np.array([0.78084, 0.20946, 0.009340, 0.00036])
    m = air.masses(yk)

    # Compute the solubility of nitrogen at the air-water interface, then
    # correct for seawater compressibility
    n2_conc = np.zeros(len(profile.z))
    for i in range(len(profile.z)):
        T, S, P = profile.get_values(profile.z[i],
                                     ['temperature', 'salinity', 'pressure'])
        Cs = air.solubility(m, T, 101325., S)[0,:] * \
             seawater.density(T, S, P) / seawater.density(T, S, 101325.)
        n2_conc[i] = Cs[0]

    # Add this computed nitrogen profile to the Profile dataset
    data = np.vstack((profile.z, n2_conc)).transpose()
    symbols = ['z', 'nitrogen']
    units = ['m', 'kg/m^3']
    comments = ['measured', 'computed from CTD data']
    profile.append(data, symbols, units, comments, 0)

    # Close the dataset
    profile.close_nc()

    # Plot the oxygen and nitrogren profiles to show that data have been
    # added to the Profile object
    z = np.linspace(profile.z_min, profile.z_max, 250)
Exemplo n.º 14
0
Arquivo: lmp.py Projeto: changks/tamoc
def zfe_volume_flux(profile, X0, R, Vj, Sj, Tj):
    """
    Compute the volume flux of continous phase discharge fluid at the release
    
    If the release includes continous phase fluid, this function computes
    the flow rate and geometry of the release
    
    Parameters
    ----------
    profile : `ambient.Profile` object
        The ambient CTD object used by the single bubble model simulation.
    X0 : ndarray
        Release location (x, y, z) in (m)
    R : float
        Radius for the equivalent circular cross-section of the release 
        (m)
    Vj : float
        Scalar value of the magnitude of the discharge velocity for 
        continuous phase fluid in the discharge.  This variable should be 
        0 or None for a pure multiphase discharge.
    Sj : float
        Salinity of the continuous phase fluid in the discharge (psu)
    Tj : float
        Temperature of the continuous phase fluid in the discharge (T)
    
    Returns
    -------
    Q : Volume flux of continuous phase fluid at the discharge (m^3/s)
    A : Cross-sectional area of the discharge (M^2)
    X : ndarray
        Release location (x, y, z) in (m)
    Tj : float
        Temperature of the continuous phase fluid in the discharge (T)
    Sj : float
        Salinity of the continuous phase fluid in the discharge (psu)
    Pj : float
        Pressure at the discharge (Pa)
    rho_j : float
        Density of the continous phase fluid in the discharge (kg/m^3)
    
    """
    # The Lagrangian plume model starts at the discharge.
    X = X0

    # Get the jet density from the discharge characteristics
    Ta, Sa, P = profile.get_values(X[2],
                                   ['temperature', 'salinity', 'pressure'])
    rho_j = seawater.density(Ta, Sa, P)

    # Pressure at the discharge is the ambient pressure
    Pj = P

    # The discharge area if the full port area
    A = np.pi * R**2

    # Compute the volume flux of discharge fluid
    Q = A * Vj

    # Return the initial conditions with salinity and temperature of the
    # discharge equal to the jet values
    return (Q, A, X, Tj, Sj, Pj, rho_j)
Exemplo n.º 15
0
 def residual(Sa, rho, H):
     """
     docstring for residual
     
     """
     return rho - seawater.density(Ta, Sa, Pa + rho_w * g * H)
Exemplo n.º 16
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 `bent_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
    x = 0.
    y = 0.
    z = 0.

    # 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 = bent_plume_model.Particle(x, y, z, bub, m0, T, nb0,
        lambda_1, P, Sa, Ta, K, Kt, fdis)

    # Check if the initialized attributes are correct
    assert bub_obj.integrate == True
    assert bub_obj.sim_stored == False
    assert bub_obj.farfield == False
    assert bub_obj.t == 0.
    assert bub_obj.x == x
    assert bub_obj.y == y
    assert bub_obj.z == z
    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

    # Test the bub_obj.outside() method
    bub_obj.outside(Ta, Sa, P)
    assert bub_obj.us == 0.
    assert bub_obj.rho_p == seawater.density(Ta, Sa, P)
    assert bub_obj.A == 0.
    assert_array_almost_equal(bub_obj.Cs, np.zeros(len(composition)))
    assert_array_almost_equal(bub_obj.beta, np.zeros(len(composition)))
    assert bub_obj.beta_T == 0.
    assert bub_obj.T == Ta

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

    # No need to test the bub_obj.track(), bub_obj.run_sbm() since they will
    # be tested below for the simulation cases.

    # Check functionality of insoluble particle
    drop = dbm.InsolubleParticle(isfluid=True, iscompressible=True)
    m0 = drop.mass_by_diameter(de, T, P, Sa, Ta)
    drop_obj = bent_plume_model.Particle(x, y, z, 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
Exemplo n.º 17
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
Exemplo n.º 18
0
    # Get the ambient.Profile object with the original CTD data
    ctd = get_ctd_profile()

    # Print the buoyancy frequency at a few selected depths
    z = np.array([500., 1000., 1500.])
    N = ctd.buoyancy_frequency(z)
    print('Buoyancy frequency is: ')
    for i in range(len(z)):
        print('    N(%d m) = %g (1/s) ' % (z[i], N[i]))

    # Plot the potential density profile and corresponding buoyancy frequency
    z_min = ctd.nc.variables['z'].valid_min
    z_max = ctd.nc.variables['z'].valid_max
    z = np.linspace(z_min, z_max, 500)
    ts = ctd.get_values(z, ['temperature', 'salinity'])
    rho = seawater.density(ts[:, 0], ts[:, 1], 101325.)
    N = ctd.buoyancy_frequency(z)
    fig = plt.figure(1)
    plt.clf()
    ax1 = plt.subplot(121)
    ax1.plot(rho, z)
    ax1.set_xlabel('Potential density, (kg/m^3)')
    ax1.set_ylabel('Depth, (m)')
    ax1.set_ylim([0., 2500.])
    ax1.invert_yaxis()
    ax2 = plt.subplot(122)
    ax2.plot(N, z)
    ax2.set_xlabel('N, (1/s)')
    ax2.set_ylim([0., 2500.])
    ax2.invert_yaxis()
    plt.show()
Exemplo n.º 19
0
 # Get the ambient.Profile object with the original CTD data
 ctd = get_ctd_profile()
 
 # Print the buoyancy frequency at a few selected depths
 z = np.array([500., 1000., 1500.])
 N = ctd.buoyancy_frequency(z)
 print 'Buoyancy frequency is: '
 for i in range(len(z)):
     print '    N(%d m) = %g (1/s) ' % (z[i], N[i])
 
 # Plot the potential density profile and corresponding buoyancy frequency
 z_min = ctd.nc.variables['z'].valid_min
 z_max = ctd.nc.variables['z'].valid_max
 z = np.linspace(z_min, z_max, 500)
 ts = ctd.get_values(z, ['temperature', 'salinity'])
 rho = seawater.density(ts[:,0], ts[:,1], 101325.)
 N = ctd.buoyancy_frequency(z)
 fig = plt.figure(1)
 plt.clf()
 ax1 = plt.subplot(121)
 ax1.plot(rho, z)
 ax1.set_xlabel('Potential density, (kg/m^3)')
 ax1.set_ylabel('Depth, (m)')
 ax1.set_ylim([0., 2500.])
 ax1.invert_yaxis()
 ax2 = plt.subplot(122)
 ax2.plot(N, z)
 ax2.set_xlabel('N, (1/s)')
 ax2.set_ylim([0., 2500.])
 ax2.invert_yaxis()
 plt.show()
Exemplo n.º 20
0
 # Get the ambient.Profile object with the original CTD data
 profile = get_ctd_profile()
 
 # Compute a dissolved nitrogen profile...start with a model for air
 air = dbm.FluidMixture(['nitrogen', 'oxygen', 'argon', 'carbon_dioxide'])
 yk = np.array([0.78084, 0.20946, 0.009340, 0.00036])
 m = air.masses(yk)
 
 # Compute the solubility of nitrogen at the air-water interface, then 
 # correct for seawater compressibility
 n2_conc = np.zeros(len(profile.z))
 for i in range(len(profile.z)):
     T, S, P = profile.get_values(profile.z[i], ['temperature', 'salinity', 
               'pressure'])
     Cs = air.solubility(m, T, 101325., S)[0,:] * \
          seawater.density(T, S, P) / seawater.density(T, S, 101325.)
     n2_conc[i] = Cs[0]
 
 # Add this computed nitrogen profile to the Profile dataset
 data = np.vstack((profile.z, n2_conc)).transpose()
 symbols = ['z', 'nitrogen']
 units = ['m', 'kg/m^3']
 comments = ['measured', 'computed from CTD data']
 profile.append(data, symbols, units, comments, 0)
 
 # Close the dataset
 profile.close_nc()
 
 # Plot the oxygen and nitrogren profiles to show that data have been 
 # added to the Profile object
 z = np.linspace(profile.z_min, profile.z_max, 250)
Exemplo n.º 21
0
def outer_cpic(yi, yo, particles, profile, p, neighbor, z_0):
    """
    Compute the initial condition for the outer plume at depth
    
    Computes the initial conditions for the an outer plume segment within the 
    reservoir body.  Part of the calculation determines whether or not the 
    computed initial condition has enough downward momentum to be viable as 
    an initial condition (e.g., whether or not it will be overwhelmed by the
    upward drag of the inner plume).
    
    Parameters
    ----------
    yi : `stratified_plume_model.InnerPlume` object
        Object for manipulating the inner plume state space.
    yo : `stratified_plume_model.OuterPlume` object
        Object for manipulating the outer plume state space.
    particles : list of `Particle` objects
        List of `Particle` objects containing the dispersed phase local
        conditions and behavior.
    profile : `ambient.Profile` object
        The ambient CTD object used by the simulation.
    p : `ModelParams` object
        Object containing the fixed model parameters for the stratified 
        plume model.
    neighbor : `scipy.interpolate.interp1d` object
        Container holding the latest solution for the inner plume state
        space.
    z_0 : float
        Top of the inner plume calculation (m).
    
    Returns
    -------
    z0 : float
        Initial depth of the outer plume segment (m).
    y0 : ndarray
        Initial dependent variables state space for the outer plume segment.
    flag : bool
        Outer plume viability flag:  `True` means the outer plume segment is
        viable and should be integrated; `False` means the outer plume 
        segment is too weak and should be discarded, moving down the inner 
        plume to calculate the next outer plume initial condition.
    
    Notes
    -----
    The iteration required to find a viable outer plume segment is conducted 
    by the `stratified_plume_model.outer_main` function.  This function 
    computes the initial conditions for one attempt to find an outer plume
    segment and reports back (through `flag`) on the success.
    
    There is one caveat to the above statement.  The model parameter 
    `p.nwidths` determines the vertical scale over which this function may
    integrate to find the start to an outer plume, given as a integer number
    of times of the inner plume half-width.  This function starts by searching
    one half-width.  If `p.nwidths` is greater than one, it will continue to
    expand the search region.  The physical interpretation of `p.nwidths` is
    to set a reasonable upper bound on the diameter of eddies shed from the
    inner plume in the peeling region into the outer plume.  While the 
    integral model does not have "eddies" per se, the search window size 
    should still be representative of this type of length scale.  
    
    """
    # Start the iteration counters
    iter = 0
    done = False

    # Compute the outer plume initial conditions until the outer plume is
    # viable or until the maximum number of widths is integrated
    while not done and iter < p.nwidths:

        # Update iteration counter
        iter += 1

        # Get the inner plume properties at the top of this peeling region
        yi.update(z_0, neighbor(z_0), particles, profile, p)

        # Set the range to integrate to get the current peeling flux
        z_upper = z_0
        z_lower = z_0 + iter * yi.b

        # Check if the bottom of the reservoir is encountered.
        if z_lower > profile.z_max:
            z_lower = profile.z_max

        # Find the indices in the raw data for the inner plume solution close
        # to where z_upper and z_lower occur
        i_upper = np.min(np.where(neighbor.x >= z_upper)[0])
        i_lower = np.max(np.where(neighbor.x <= z_lower)[0])

        # Get the grid of elevations where we will integrate the solution to
        # obtain the initial flux for the outer plume.  This is needed
        # because the solution is so stiff:  if we integrated over a fixed
        # step size, we could easily miss dramatic changes in the solution.
        # Hence, we integrate over the steps in the numerical solution
        # itself.
        n_grid = i_lower - i_upper + 3
        zi = np.zeros(n_grid)
        zi[0] = z_upper
        zi[-1] = z_lower
        zi[1:-1] = neighbor.x[i_upper:i_lower + 1]

        # Integrate the peeling fluid over this grid to get the total
        # contributions going into the outer plume
        Q = 0.
        tracer_vars = np.zeros(2 + yi.nchems)
        for i in range(len(zi) - 1):
            yi.update(zi[i], neighbor(zi[i]), particles, profile, p)
            dz = zi[i + 1] - zi[i]
            Q = Q + yi.Ep * dz
            tracer_vars = tracer_vars + np.hstack((yi.s, yi.T * p.rho_r * \
                          seawater.cp(), yi.c)) * yi.Ep * dz

        # Get the initial velocity of the peeling fluid using the modified
        # outer plume Froude number condition
        T = tracer_vars[1] / (Q * p.rho_r * seawater.cp())
        s = tracer_vars[0] / Q
        c = tracer_vars[2:] / Q
        rho = seawater.density(T, s, yi.P)
        u = outer_fr(0.05, -Q, yi.b, yi.rho_a, rho, p.g, p.Fro_0)
        b = np.sqrt(Q**2 / (np.pi * (-Q) * u) + yi.b**2)
        dQdz = 2. * np.pi * yi.b * (p.alpha_1 * (yi.u + p.c1 * (-u)) + \
                p.alpha_2 * (-u)) + 2. * np.pi * b * p.alpha_3 * (-u) + yi.Ep

        # Check whether this outer plume segment will be viable
        if dQdz > 0 or Q > 0 or np.isnan(Q):
            # This outer plume segment is not viable
            flag = False
            z0 = np.array([z_0, z_lower])
            y0 = np.array([np.zeros(yo.len), np.zeros(yo.len)])

        else:
            # This outer plume segmet is viable...stop integrating widths
            done = True

            # Check where the diffuser is
            if z_lower >= yi.z0:
                # This outer plume segment should not exist
                flag = False
                z0 = np.array([z_0, z_lower])
                y0 = np.array([np.zeros(yo.len), np.zeros(yo.len)])

            else:
                # This is the next outer plume segment to integrate
                flag = True
                z0 = z_lower
                y0 = []
                y0.append(Q)
                y0.append(Q * (-u))
                y0.append(s * Q)
                y0.append(p.rho_r * seawater.cp() * T * Q)
                y0.extend(c * Q)

    # Return the results of the initial conditions search
    return (z0, np.array(y0), flag)
Exemplo n.º 22
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 `bent_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
    x = 0.
    y = 0.
    z = 0.
    
    # 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 = bent_plume_model.Particle(x, y, z, bub, m0, T, nb0, 
        lambda_1, P, Sa, Ta, K, Kt, fdis)
    
    # Check if the initialized attributes are correct
    assert bub_obj.integrate == True
    assert bub_obj.sim_stored == False
    assert bub_obj.farfield == False
    assert bub_obj.t == 0.
    assert bub_obj.x == x
    assert bub_obj.y == y
    assert bub_obj.z == z
    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
    
    # Test the bub_obj.outside() method
    bub_obj.outside(Ta, Sa, P)
    assert bub_obj.us == 0.
    assert bub_obj.rho_p == seawater.density(Ta, Sa, P)
    assert bub_obj.A == 0.
    assert_array_almost_equal(bub_obj.Cs, np.zeros(len(composition)))
    assert_array_almost_equal(bub_obj.beta, np.zeros(len(composition)))
    assert bub_obj.beta_T == 0.
    assert bub_obj.T == Ta
    
    # No need to test the properties or diameter objects since they are 
    # inherited from the `single_bubble_model` and tested in `test_sbm`.
    
    # No need to test the bub_obj.track(), bub_obj.run_sbm() since they will
    # be tested below for the simulation cases.  
    
    # Check functionality of insoluble particle 
    drop = dbm.InsolubleParticle(isfluid=True, iscompressible=True)
    m0 = drop.mass_by_diameter(de, T, P, Sa, Ta)
    drop_obj = bent_plume_model.Particle(x, y, z, 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
Exemplo n.º 23
0
    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)
Exemplo n.º 24
0
    # Create an ambient.Profile object for this dataset
    roms = ambient.Profile(nc, chem_names=['dye_01', 'dye_02'])

    # Close the netCDF dataset
    roms.nc.close()

    # Since the netCDF file is now fully stored on the hard drive in the
    # correct format, we can initialize an ambient.Profile object directly
    # from the netCDF file
    roms = ambient.Profile(nc_file, chem_names='all')

    # Plot the density profile using the interpolation function
    z = np.linspace(roms.nc.variables['z'].valid_min,
                    roms.nc.variables['z'].valid_max, 250)
    rho = np.zeros(z.shape)
    tsp = roms.get_values(z, ['temperature', 'salinity', 'pressure'])
    for i in range(len(z)):
        rho[i] = seawater.density(tsp[i, 0], tsp[i, 1], tsp[i, 2])

    fig = plt.figure()
    ax1 = plt.subplot(121)
    ax1.plot(rho, z)
    ax1.set_xlabel('Density (kg/m^3)')
    ax1.set_ylabel('Depth (m)')
    ax1.invert_yaxis()
    ax1.set_title('Computed data')
    plt.show()

    # Close the netCDF dataset
    roms.nc.close()
Exemplo n.º 25
0
def get_profile(profile_fname, z0, D, uj, vj, wj, Tj, Sj, ua, T, F, sign_dp,
                H):
    """
    Create and ambient profile dataset for an integral jet model simulation
    
    """
    # Use mks units
    g = 9.81
    rho_w = 1000.
    Pa = 101325.

    # Get the ambient density at the discharge from F (Assume water is
    # incompressible for these laboratory experiments)
    rho_j = seawater.density(Tj, Sj, 101325. + rho_w * g * z0)
    Vj = np.sqrt(uj**2 + vj**2 + wj**2)
    if F == 0.:
        rho_a = rho_j
    else:
        rho_a = rho_j / (1. - Vj**2 / (sign_dp * F**2 * D * g))

    # Get the ambient stratification at the discharge from T
    if T == 0.:
        dpdz = 0.
    else:
        dpdz = sign_dp * (rho_a - rho_j) / (T * D)

    # Find the salinity at the discharge assuming the discharge temperature
    # matches the ambient temperature
    Ta = Tj

    def residual(Sa, rho, H):
        """
        docstring for residual
        
        """
        return rho - seawater.density(Ta, Sa, Pa + rho_w * g * H)

    Sa = fsolve(residual, 0., args=(rho_a, z0))

    # Find the salinity at the top and bottom assuming linear stratification
    if dpdz == 0.:
        S0 = Sa
        SH = Sa
    else:
        rho_H = dpdz * (H - z0) + rho_a
        rho_0 = dpdz * (0. - z0) + rho_a
        # Use potential density to get the salinity
        SH = fsolve(residual, Sa, args=(rho_H, z0))
        S0 = fsolve(residual, Sa, args=(rho_0, z0))

    # Build the ambient data arrays
    z = np.array([0., H])
    T = np.array([Ta, Ta])
    S = np.array([S0, SH])
    ua = np.array([ua, ua])

    # Build the profile
    profile = build_profile(profile_fname, z, T, S, ua)

    # Return the ambient data
    return profile
Exemplo n.º 26
0
def zfe_volume_flux(profile, X0, R, Vj, Sj, Tj):
    """
    Compute the volume flux of continous phase discharge fluid at the release
    
    If the release includes continous phase fluid, this function computes
    the flow rate and geometry of the release
    
    Parameters
    ----------
    profile : `ambient.Profile` object
        The ambient CTD object used by the single bubble model simulation.
    X0 : ndarray
        Release location (x, y, z) in (m)
    R : float
        Radius for the equivalent circular cross-section of the release 
        (m)
    Vj : float
        Scalar value of the magnitude of the discharge velocity for 
        continuous phase fluid in the discharge.  This variable should be 
        0 or None for a pure multiphase discharge.
    Sj : float
        Salinity of the continuous phase fluid in the discharge (psu)
    Tj : float
        Temperature of the continuous phase fluid in the discharge (T)
    
    Returns
    -------
    Q : Volume flux of continuous phase fluid at the discharge (m^3/s)
    A : Cross-sectional area of the discharge (M^2)
    X : ndarray
        Release location (x, y, z) in (m)
    Tj : float
        Temperature of the continuous phase fluid in the discharge (T)
    Sj : float
        Salinity of the continuous phase fluid in the discharge (psu)
    Pj : float
        Pressure at the discharge (Pa)
    rho_j : float
        Density of the continous phase fluid in the discharge (kg/m^3)
    
    """
    # The Lagrangian plume model starts at the discharge.
    X = X0
    
    # Get the jet density from the discharge characteristics
    Ta, Sa, P = profile.get_values(X[2], ['temperature', 'salinity', 
        'pressure'])
    rho_j = seawater.density(Ta, Sa, P)
    
    # Pressure at the discharge is the ambient pressure
    Pj = P
    
    # The discharge area if the full port area
    A = np.pi * R**2
    
    # Compute the volume flux of discharge fluid
    Q = A * Vj
    
    # Return the initial conditions with salinity and temperature of the 
    # discharge equal to the jet values
    return (Q, A, X, Tj, Sj, Pj, rho_j)
Exemplo n.º 27
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)
Exemplo n.º 28
0
def outer_cpic(yi, yo, particles, profile, p, neighbor, z_0):
    """
    Compute the initial condition for the outer plume at depth
    
    Computes the initial conditions for the an outer plume segment within the 
    reservoir body.  Part of the calculation determines whether or not the 
    computed initial condition has enough downward momentum to be viable as 
    an initial condition (e.g., whether or not it will be overwhelmed by the
    upward drag of the inner plume).
    
    Parameters
    ----------
    yi : `stratified_plume_model.InnerPlume` object
        Object for manipulating the inner plume state space.
    yo : `stratified_plume_model.OuterPlume` object
        Object for manipulating the outer plume state space.
    particles : list of `Particle` objects
        List of `Particle` objects containing the dispersed phase local
        conditions and behavior.
    profile : `ambient.Profile` object
        The ambient CTD object used by the simulation.
    p : `ModelParams` object
        Object containing the fixed model parameters for the stratified 
        plume model.
    neighbor : `scipy.interpolate.interp1d` object
        Container holding the latest solution for the inner plume state
        space.
    z_0 : float
        Top of the inner plume calculation (m).
    
    Returns
    -------
    z0 : float
        Initial depth of the outer plume segment (m).
    y0 : ndarray
        Initial dependent variables state space for the outer plume segment.
    flag : bool
        Outer plume viability flag:  `True` means the outer plume segment is
        viable and should be integrated; `False` means the outer plume 
        segment is too weak and should be discarded, moving down the inner 
        plume to calculate the next outer plume initial condition.
    
    Notes
    -----
    The iteration required to find a viable outer plume segment is conducted 
    by the `stratified_plume_model.outer_main` function.  This function 
    computes the initial conditions for one attempt to find an outer plume
    segment and reports back (through `flag`) on the success.
    
    There is one caveat to the above statement.  The model parameter 
    `p.nwidths` determines the vertical scale over which this function may
    integrate to find the start to an outer plume, given as a integer number
    of times of the inner plume half-width.  This function starts by searching
    one half-width.  If `p.nwidths` is greater than one, it will continue to
    expand the search region.  The physical interpretation of `p.nwidths` is
    to set a reasonable upper bound on the diameter of eddies shed from the
    inner plume in the peeling region into the outer plume.  While the 
    integral model does not have "eddies" per se, the search window size 
    should still be representative of this type of length scale.  
    
    """
    # Start the iteration counters
    iter = 0
    done = False
    
    # Compute the outer plume initial conditions until the outer plume is 
    # viable or until the maximum number of widths is integrated
    while not done and iter < p.nwidths:
        
        # Update iteration counter
        iter += 1
        
        # Get the inner plume properties at the top of this peeling region
        yi.update(z_0, neighbor(z_0), particles, profile, p)
        
        # Set the range to integrate to get the current peeling flux
        z_upper = z_0
        z_lower = z_0 + iter * yi.b
        
        # Check if the bottom of the reservoir is encountered.
        if z_lower > profile.z_max:
            z_lower = profile.z_max
        
        # Find the indices in the raw data for the inner plume solution close
        # to where z_upper and z_lower occur
        i_upper = np.min(np.where(neighbor.x >= z_upper)[0])
        i_lower = np.max(np.where(neighbor.x <= z_lower)[0])
        
        # Get the grid of elevations where we will integrate the solution to 
        # obtain the initial flux for the outer plume.  This is needed 
        # because the solution is so stiff:  if we integrated over a fixed
        # step size, we could easily miss dramatic changes in the solution.
        # Hence, we integrate over the steps in the numerical solution 
        # itself.
        n_grid = i_lower - i_upper + 3
        zi = np.zeros(n_grid)
        zi[0] = z_upper
        zi[-1] = z_lower
        zi[1:-1] = neighbor.x[i_upper:i_lower+1]
        
        # Integrate the peeling fluid over this grid to get the total 
        # contributions going into the outer plume
        Q = 0.
        tracer_vars = np.zeros(2 + yi.nchems)
        for i in range(len(zi)-1):
            yi.update(zi[i], neighbor(zi[i]), particles, profile, p)
            dz = zi[i+1] - zi[i]
            Q = Q + yi.Ep * dz
            tracer_vars = tracer_vars + np.hstack((yi.s, yi.T * p.rho_r * \
                          seawater.cp(), yi.c)) * yi.Ep * dz
        
        # Get the initial velocity of the peeling fluid using the modified 
        # outer plume Froude number condition
        T = tracer_vars[1] / (Q * p.rho_r * seawater.cp())
        s = tracer_vars[0] / Q
        c = tracer_vars[2:] / Q
        rho = seawater.density(T, s, yi.P)
        u = outer_fr(0.05, -Q, yi.b, yi.rho_a, rho, p.g, p.Fro_0)
        b = np.sqrt(Q**2 / (np.pi * (-Q) * u) + yi.b**2)
        dQdz = 2. * np.pi * yi.b * (p.alpha_1 * (yi.u + p.c1 * (-u)) + \
                p.alpha_2 * (-u)) + 2. * np.pi * b * p.alpha_3 * (-u) + yi.Ep
        
        # Check whether this outer plume segment will be viable
        if dQdz > 0 or Q > 0 or np.isnan(Q):
            # This outer plume segment is not viable
            flag = False
            z0 = np.array([z_0, z_lower])
            y0 = np.array([np.zeros(yo.len), np.zeros(yo.len)])
        
        else:
            # This outer plume segmet is viable...stop integrating widths
            done = True
            
            # Check where the diffuser is
            if z_lower >= yi.z0:
                # This outer plume segment should not exist
                flag = False
                z0 = np.array([z_0, z_lower])
                y0 = np.array([np.zeros(yo.len), np.zeros(yo.len)])
            
            else:
                # This is the next outer plume segment to integrate
                flag = True
                z0 = z_lower
                y0 = []
                y0.append(Q)
                y0.append(Q * (-u))
                y0.append(s * Q)
                y0.append(p.rho_r * seawater.cp() * T * Q)
                y0.extend(c * Q)
    
    # Return the results of the initial conditions search
    return (z0, np.array(y0), flag)
Exemplo n.º 29
0
 # 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)
 T = 273.15 + 60.
 print '\nBasic properties of liquid oil: \n'
 print '   T = %g (K)' % T
 print '   mol_frac = [' + ', '.join('%g' % mol_frac[i] for i in 
     range(oil.nc)) + '] (--)'
 print '   mass_frac = [' + ', '.join('%g' % mf[i] for i in 
     range(oil.nc)) + '] (--)'
 print '   rho_p = %g (kg/m^3) at %g (K) and %g (Pa)' % \
     (oil.density(mf, T, P), T, P)
 
 # Get the masses in a 1.0 cm effective diameter droplet
 de = 0.01
Exemplo n.º 30
0
                                iscompressible,
                                gamma=gamma,
                                beta=beta,
                                co=co)

    # 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
    T = 273.15 + 60.
    print '\nBasic properties of liquid oil: \n'
    print '   T = %g (K)' % T
    print '   rho_p = %g (kg/m^3) at %g (K) and %g (Pa)' % \
        (oil.density(T, P, Sa, Ta), T, P)

    # Get the masses in a 1.0 cm effective diameter droplet
    de = 0.01
    m = oil.mass_by_diameter(de, T, P, Sa, Ta)

    # Echo the properties of the droplet to the screen
    print '\nBasic droplet properties:  \n'
    print '   de = %g (m)' % (oil.diameter(m, T, P, Sa, Ta))