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
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
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)
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)
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)
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
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)
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
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)
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)
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)
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',
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
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
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)
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,