def dissolve_oil(self, data, substance, **kwargs): ''' Here is where we calculate the dissolved oil. We will outline the steps as we go along, but off the top of my head: - recalculate the partition coefficient (K_ow) TODO: This requires a molar average of the aromatic components. - use VDROP to calculate the shift in the droplet distribution - for each droplet size category: - calculate the water phase transfer velocity (k_w) (Stokes) - calculate the mass xfer rate coefficient (beta) - calculate the water column time fraction (f_wc) - calculate the volume dissolved - subtract the mass of smallest droplets in our distribution that are below a threshold. ''' fmass = data['mass_components'] arom_mask = substance._sara['type'] == 'Aromatics' mol_wt = substance.molecular_weight rho = substance.component_density assert mol_wt.shape == rho.shape # calculate the partition coefficient (K_ow) for all aromatics. # K_ow for non-aromatics should be masked to 0.0 K_ow_comp = arom_mask * LeeHuibers.partition_coeff(mol_wt, rho) for idx, m in enumerate(fmass): K_ow = (np.sum(m * K_ow_comp / mol_wt) / np.sum(m / mol_wt)) data['partition_coeff'][idx] = K_ow diss = np.zeros((len(data['mass'])), dtype=np.float64) return diss
def test_lee_huibers(): assert np.isclose(LeeHuibers.partition_coeff(92.1, 866.0), 1000)
def dissolve_oil(self, data, substance, **kwargs): ''' Here is where we calculate the dissolved oil. We will outline the steps as we go along, but off the top of my head: - recalculate the partition coefficient (K_ow) - droplet distribution per LE should be calculated by the natural dispersion process and saved in the data arrays before the dissolution weathering process. - for each LE: (Note: right now the natural dispersion process only calculates a single average droplet size. But we still treat it as an iterable.) - for each droplet size category: - calculate the water phase transfer velocity (k_w) - calculate the mass xfer rate coefficient (beta) - calculate the water column time fraction (f_wc) - calculate the mass dissolved during refloat period - calculate the mass dissolved from the slick during the calm period. - the mass dissolved in the water column and the slick is summed per mass fraction (should only be aromatic fractions) - the sum of dissolved masses are compared to the existing mass fractions and adjusted to make sure we don't dissolve more mass than exists in the mass fractions. ''' model_time = kwargs.get('model_time') time_step = kwargs.get('time_step') fmasses = data['mass_components'] droplet_avg_sizes = data['droplet_avg_size'] areas = data['area'] arom_mask = substance._sara['type'] == 'Aromatics' mol_wt = substance.molecular_weight rho = substance.component_density assert mol_wt.shape == rho.shape # calculate the partition coefficient (K_ow) for all aromatics # for each LE. # K_ow for non-aromatics are masked to 0.0 K_ow_comp = arom_mask * LeeHuibers.partition_coeff(mol_wt, rho) data['partition_coeff'] = ((fmasses * K_ow_comp / mol_wt).sum(axis=1) / (fmasses / mol_wt).sum(axis=1)) avg_rhos = self.oil_avg_density(fmasses, rho) water_rhos = np.zeros(avg_rhos.shape) + self.waves.water.get('density') k_w_i = Stokes.water_phase_xfer_velocity(water_rhos - avg_rhos, droplet_avg_sizes) total_volumes = self.oil_total_volume(fmasses, rho) X_i, S_RA_volumes = self.state_variable(fmasses, rho, arom_mask) beta_i = self.beta_coeff(k_w_i, data['partition_coeff'], S_RA_volumes) dX_dt_i = beta_i * X_i / (X_i + 1.0) ** (1.0 / 3.0) f_wc_i = self.water_column_time_fraction(model_time, k_w_i) T_wc_i = f_wc_i * time_step T_calm_i = self.calm_between_wave_breaks(model_time, time_step, T_wc_i) assert np.alltrue(T_calm_i <= float(time_step)) assert np.alltrue(T_wc_i <= float(time_step)) assert np.alltrue(T_wc_i + T_calm_i <= float(time_step)) aromatic_masses_i = fmasses * arom_mask aromatic_fractions_i = (aromatic_masses_i.T / aromatic_masses_i.sum(axis=1)).T # # OK, here it is, the mass dissolved in the water column. # mass_dissolved_in_wc = np.nan_to_num((aromatic_fractions_i.T * dX_dt_i * T_wc_i).T) oil_concentrations = self.oil_concentration(fmasses, rho) N_s_i = self.slick_subsurface_mass_xfer_rate(model_time, oil_concentrations, K_ow_comp, areas, arom_mask) # # OK, here it is, the mass dissolved in the slick. # mass_dissolved_in_slick = (N_s_i.T * T_calm_i).T mass_dissolved_in_wc = np.vstack(mass_dissolved_in_wc) mass_dissolved_in_slick = np.vstack(mass_dissolved_in_slick) total_mass_dissolved = mass_dissolved_in_wc + mass_dissolved_in_slick # adjust any masses that might go negative total_mass_dissolved += np.clip(fmasses - total_mass_dissolved, -np.inf, 0.0) return total_mass_dissolved