def _run_tamoc(self): """ this is the code that actually calls and runs tamoc_output it returns a list of TAMOC droplet objects """ # Release conditions tp = self.tamoc_parameters # Release depth (m) z0 = tp['depth'] # Release diameter (m) D = tp['diameter'] # Release flowrate (bpd) Q = tp['release_flowrate'] # Release temperature (K) T0 = tp['release_temp'] # Release angles of the plume (radians) phi_0 = tp['release_phi'] theta_0 = tp['release_theta'] # Salinity of the continuous phase fluid in the discharge (psu) S0 = tp['discharge_salinity'] # Concentration of passive tracers in the discharge (user-defined) c0 = tp['tracer_concentration'] # List of passive tracers in the discharge chem_name = 'tracer' # Presence or abscence of hydrates in the particles hydrate = tp['hydrate'] # Prescence or abscence of dispersant dispersant = tp['dispersant'] # Reduction in interfacial tension due to dispersant sigma_fac = tp['sigma_fac'] # sigma_fac[0] - for gas; sigma_fac[1] - for liquid # Define liquid phase as inert inert_drop = tp['inert_drop'] # d_50 of gas particles (m) d50_gas = tp['d50_gas'] # d_50 of oil particles (m) d50_oil = tp['d50_oil'] # number of bins in the particle size distribution nbins = tp['nbins'] # Create the ambient profile needed for TAMOC # name of the nc file nc_file = tp['nc_file'] # Define and input the ambient ctd profiles fname_ctd = tp['fname_ctd'] # Define and input the ambient velocity profile ua = tp['ua'] va = tp['va'] wa = tp['wa'] depths = tp['depths'] profile = self.get_profile(nc_file, fname_ctd, ua, va, wa, depths) # Get the release fluid composition fname_composition = './Input/API_2000.csv' composition, mass_frac = self.get_composition(fname_composition) # Read in the user-specified properties for the chemical data data, units = chem.load_data('./Input/API_ChemData.csv') oil = dbm.FluidMixture(composition, user_data=data) # Get the release rates of gas and liquid phase md_gas, md_oil = self.release_flux(oil, mass_frac, profile, T0, z0, Q) print 'md_gas, md_oil', np.sum(md_gas), np.sum(md_oil) # Get the particle list for this composition particles = self.get_particles(composition, data, md_gas, md_oil, profile, d50_gas, d50_oil, nbins, T0, z0, dispersant, sigma_fac, oil, mass_frac, hydrate, inert_drop) print len(particles) print particles # Run the simulation jlm = bpm.Model(profile) jlm.simulate(np.array([0., 0., z0]), D, None, phi_0, theta_0, S0, T0, c0, chem_name, particles, track=False, dt_max=60., sd_max=6000.) # Update the plume object with the nearfiled terminal level answer jlm.q_local.update(jlm.t[-1], jlm.q[-1], jlm.profile, jlm.p, jlm.particles) Mp = np.zeros((len(jlm.particles), len(jlm.q_local.M_p[0]))) gnome_particles = [] gnome_diss_components = [] # print jlm.particles m_tot_nondiss = 0. for i in range(len(jlm.particles)): nb0 = jlm.particles[i].nb0 Tp = jlm.particles[i].T Mp[i, 0:len(jlm.q_local.M_p[i])] = jlm.q_local.M_p[i][:] / jlm.particles[i].nbe mass_flux = np.sum(Mp[i, :] * jlm.particles[i].nb0) density = jlm.particles[i].rho_p radius = (jlm.particles[i].diameter(Mp[i, 0:len(jlm.particles[i].m)], Tp, jlm.q_local.Pa, jlm.q_local.S, jlm.q_local.T)) / 2. position = np.array([jlm.particles[i].x, jlm.particles[i].y, jlm.particles[i].z]) # Calculate the equlibrium and get the particle phase Eq_parti = dbm.FluidMixture(composition=jlm.particles[i].composition[:], user_data=data) # Get the particle equilibrium at the plume termination conditions print 'Insitu' flag_phase_insitu = self.get_phase(jlm.profile, Eq_parti, Mp[i, :]/np.sum(Mp[i, :]), Tp, jlm.particles[i].z) # Get the particle equilibrium at the 15 C and 1 atm print 'Surface' flag_phase_surface = self.get_phase(jlm.profile, Eq_parti, Mp[i, :]/np.sum(Mp[i, :]), 273.15 + 15. , 0.) gnome_particles.append(TamocDroplet(mass_flux, radius, density, position)) for p in gnome_particles: print p m_tot_diss = 0. # Calculate the dissolved particle flux for j in range(len(jlm.chem_names)): diss_mass_flux = jlm.q_local.c_chems[j] * np.pi * jlm.q_local.b**2 * jlm.q_local.V m_tot_diss += diss_mass_flux # print diss_mass_flux position = np.array([jlm.q_local.x, jlm.q_local.y, jlm.q_local.z]) # print position chem_name = jlm.q_local.chem_names[j] # print chem_name gnome_diss_components.append(TamocDissMasses(diss_mass_flux, position,chem_name)) print 'total dissolved mass flux at plume termination' ,m_tot_diss print 'total non ddissolved mass flux at plume termination', m_tot_nondiss print 'total mass flux tracked at plume termination',m_tot_diss+m_tot_nondiss print 'total mass flux released at the orifice',np.sum(md_gas)+ np.sum(md_oil) print 'perccentsge_error', (np.sum(md_gas)+ np.sum(md_oil)-m_tot_diss-m_tot_nondiss)/(np.sum(md_gas)+ np.sum(md_oil))*100. return gnome_particles, gnome_diss_components
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)
from tamoc import dbm from tamoc import seawater import numpy as np import matplotlib.pyplot as plt if __name__ == '__main__': # Define the composition of air composition = ['nitrogen', 'oxygen', 'argon', 'carbon_dioxide'] mol_frac = np.array([0.78084, 0.20946, 0.009340, 0.00036]) # Create a DBM FluidMixture object for air...assume the default zeros # matrix for the binary interaction coefficients air = dbm.FluidMixture(composition) # Get the mass composition for one mole of air m = air.masses(mol_frac) # Calculate the density at standard conditions rho_m = air.density(m, 273.15 + 15., 101325.)[0] print '\nStandard density of air is: %g (kg/m^3)' % rho_m # Calculate the viscosity at standard conditions mu = air.viscosity(m, 273.15 + 15., 101325.)[0] print '\nStandard viscosity of air is: %g (Pa s)' % mu # Calculate the density at deepwater ocean conditions rho_m = air.density(m, 273.15 + 4., 150. * 1.e5)[0] print '\nDensity of air at 4 deg C and 150 bar is: %g (kg/m^3)' % rho_m
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
bm54 = ambient.Profile(nc, chem_names=['oxygen']) # Return the Profile object return bm54 if __name__ == '__main__': """ Demonstrate how to add data to an existing Profile object """ # 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()
def wang_etal(d0, m_g, rho_g, mu_g, sigma_g, rho, mu, m_l=0., rho_l=None, P=4.e6, T=288.15): """ Compute characteristic values for gas jet breakup Computes the characteristic gas bubble sizes for jet breakup using the equations in Wang et al. (2018) (wang_etal model equations). Parameters ---------- d0 : float Equivalent circular diameter of the release (m) m_g : np.array Mass fluxes of each pseudo-component of the gas-phase fluid at the release (kg/s) rho_g : float Density of the gas-phase fluid at the release (kg/m^3) mu_g : float Dynamic viscosity of the gas-phase fluid at the release (Pa s) sigma_g : float Interfacial tension between the gas-phase fluid and water at the release (N/m) rho : float Density of seawater at the release (kg/m^3) mu : float Dynamic viscosity of seawater at the release (Pa s) m_l : np.array Mass fluxes of each pseudo-component of the liquid-phase fluid at the release (kg/s) rho_l : float Density of the liquid-phase fluid at the release (kg/m^3) P : float, default=4.e6 Pressure in the receiving fluid (Pa); used to compute the speed of sound in the released gas. T : float, default=288.15 Temperature of the gas phase at the release (K); used to compute the speed of sound in the released gas. Returns ------- d50_gas : float Volume median diameter of the gas bubbles (m) m_gas : float Mass fluxes of each pseudo-component of the gas-phase fluid at the release (kg/s). This may be different from the input value in the case of choked flow at the orifice. m_oil : float Mass fluxes of each pseudo-component of the liquid-phase fluid at the release (kg/s). This may be different from the input value in the case of choked flow at the orifice. de_max : float Maximum stable particle size of the fluid phase of interest (m) sigma : float Standard deviation of the Log-normal distribution in logarithmic units. """ # Convert mass-flux to volume flux Qg = mass2vol(m_g, rho_g) if np.sum(m_l) == 0.: Ql = 0. else: Ql = mass2vol(m_l, rho_l) # Compute the exit velocity assuming no choked flow and single exit # velocity n = Qg / (Qg + Ql) A = np.pi * d0**2 / 4. Ug = (Qg + Ql) / A # Check for choked flow using methane for speed of sound ch4 = dbm.FluidMixture(['methane']) delta_rho = ch4.density(np.array([1.]), T, P)[0,0] - \ ch4.density(np.array([1.]), T, 1.01 * P)[0,0] a = np.sqrt((P - 1.01 * P) / delta_rho) if 10. * Ug < a: U_E = Ug else: # Compute the cp / cv ratio cp_ch4 = 35.69 # J/mol/K; CO2 = 37.13 cv_ch4 = cp_ch4 - 8.31451 # From Poling et al. for ideal gases kappa = cp_ch4 / cv_ch4 # Assume approximately ok for petroleum # Get the Mach number Ma = Ug / a # Correct the exit velocity for choked flow if Ma < np.sqrt((kappa + 1.) / 2.): U_E = a * (-1. + np.sqrt(1. + 2. * (kappa - 1.) * Ma**2.)) / \ ((kappa - 1.) * Ma) else: U_E = a * np.sqrt(2. / (kappa + 1.)) # Update the gas and oil exit velocities if Qg > 0: Ug = U_E else: Ug = 0 if Ql > 0: Ul = U_E else: Ul = 0 # Compute the particle size distribution for gas d50_gas, m_gas, m_oil, de_max, sigma = wang_etal_model( A, n, Ug, rho_g, mu_g, sigma_g, Ul, rho_l, rho, mu) return (d50_gas, m_gas, m_oil, de_max, sigma)
def get_lake_data(): """ Create the netCDF dataset of CTD data for a lake simulation Creates the ambient.Profile object and netCDF dataset of CTD data for a lake simualtion from the `./data/lake.dat` text file, digitized from the data in McGinnis et al. (2002) for Lake Hallwil. """ # Read in the lake CTD data fname = '../../tamoc/data/lake.dat' raw = np.loadtxt(fname, skiprows=9) variables = ['z', 'temperature', 'salinity', 'oxygen'] units = ['m', 'deg C', 'psu', 'kg/m^3'] # Convert the units to mks profile, units = ambient.convert_units(raw, units) # Calculate the pressure data P = ambient.compute_pressure(profile[:, 0], profile[:, 1], profile[:, 2], 0) profile = np.hstack((profile, np.atleast_2d(P).transpose())) variables = variables + ['pressure'] units = units + ['Pa'] # Set up a netCDF dataset object summary = 'Default lake.dat dataset provided by TAMOC' source = 'Lake CTD data digitized from figures in McGinnis et al. 2004' sea_name = 'Lake Hallwil, Switzerland' lat = 47.277166666666666 lon = 8.217294444444445 date = datetime(2002, 7, 18) t_units = 'seconds since 1970-01-01 00:00:00 0:00' calendar = 'julian' time = date2num(date, units=t_units, calendar=calendar) nc = ambient.create_nc_db('../../test/output/lake.nc', summary, source, sea_name, lat, lon, time) # Insert the measured data comments = ['digitized from measured data'] * 4 comments = comments + ['computed from z, T, S'] nc = ambient.fill_nc_db(nc, profile, variables, units, comments, 0) # Insert an additional column with data for nitrogen and argon equal to # their saturation concentrations at the free surface. composition = ['nitrogen', 'oxygen', 'argon'] yk = np.array([0.78084, 0.209476, 0.009684]) air = dbm.FluidMixture(composition) m = air.masses(yk) Cs = air.solubility(m, profile[0, 1], 101325., profile[0, 2]) N2 = np.zeros((profile.shape[0], 1)) Ar = np.zeros((profile.shape[0], 1)) N2 = N2 + Cs[0, 0] Ar = Ar + Cs[0, 2] z = np.atleast_2d(profile[:, 0]).transpose() comments = ['calculated potential saturation value'] * 3 nc = ambient.fill_nc_db(nc, np.hstack( (z, N2, Ar)), ['z', 'nitrogen', 'argon'], ['m', 'kg/m^3', 'kg/m^3'], comments, 0) # Create an ambient.Profile object lake = ambient.Profile(nc, chem_names=['oxygen', 'nitrogen', 'argon']) lake.close_nc() # Return the Profile object return lake