def test_at_t_0(spectral_sampling, plot=False): # Arrange settings = Settings(n_sd=64, dt=1 * si.s, n_substep=1, spectral_sampling=spectral_sampling) settings.t_max = 0 simulation = Simulation(settings) # Act output = simulation.run() # Plot if plot: from matplotlib import pyplot pyplot.step( 2e6 * settings.dry_radius_bins_edges[:-1], output['dm_S_VI/dlog_10(dry diameter)'][-1] ) pyplot.ylabel('dS(VI)/dlog_10(D)') pyplot.xlabel('dry diameter [µm]') pyplot.xscale('log') pyplot.yscale('log') pyplot.ylim([.01, 12]) pyplot.show() # Assert key = 'S_VI' spectrum = output[f'dm_{key}/dlog_10(dry diameter)'][0][0] peaks, props = find_peaks(spectrum) assert len(peaks) == 1 assert 3 < np.amax(spectrum) < 5
def test_at_cloud_base(): # Arrange settings = Settings(n_sd=50, dt=1 * si.s, n_substep=5) settings.t_max = 196 * si.s settings.output_interval = settings.dt simulation = Simulation(settings) # Act output = simulation.run() # Assert assert round(output['z'][-1]) == (698 - 600) * si.m np.testing.assert_allclose(output['p_env'][-1], 939 * si.mbar, rtol=.005) np.testing.assert_allclose(output['T_env'][-1], 284.2 * si.K, rtol=.005) np.testing.assert_allclose( settings.formulae.state_variable_triplet.rho_of_rhod_qv( rhod=output['rhod_env'][-1], qv=output['qv_env'][-1] * si.g / si.kg), 1.15 * si.kg / si.m**3, rtol=.005) assert output['ql'][-2] < .00055 assert output['ql'][-1] > .0004 assert output['RH_env'][-1] > 100 assert output['RH_env'][-8] < 100
def test_at_t_0(spectral_sampling, plot=False): # Arrange settings = Settings(n_sd=64, dt=1 * si.s, n_substep=5, spectral_sampling=spectral_sampling) settings.t_max = 0 simulation = Simulation(settings) # Act output = simulation.run() # Plot if plot: from matplotlib import pyplot pyplot.step(2e6 * settings.dry_radius_bins_edges[:-1], output['dm_S_VI/dlog_10(dry diameter)'][-1]) pyplot.ylabel('dS(VI)/dlog_10(D)') pyplot.xlabel('dry diameter [µm]') pyplot.xscale('log') pyplot.yscale('log') pyplot.ylim([.01, 12]) pyplot.show() # Assert key = 'S_VI' # TODO #481 : better than >0 (we do have analytic formula) assert (output[f'dm_{key}/dlog_10(dry diameter)'][0] > 0).any()
def test_at_1200m_above_cloud_base(): # Arrange settings = Settings(n_sd=10, dt=1 * si.s, n_substep=5) simulation = Simulation(settings) # Act output = simulation.run() # Assert np.testing.assert_allclose(output['z'][-1], (1.2 + .1) * si.km, rtol=.005) np.testing.assert_allclose(output['ql'][-1], 2.17, rtol=.02)
def test_at_t_0(): # Arrange settings = Settings(n_sd=100, dt=1 * si.s, n_substep=5) settings.t_max = 0 simulation = Simulation(settings) specific_gravities = SpecificGravities( simulation.particulator.formulae.constants) # Act output = simulation.run() # Assert np.testing.assert_allclose(output['RH'][0], 95) np.testing.assert_allclose(output['gas_S_IV_ppb'][0], 0.2) np.testing.assert_allclose(output['gas_N_mIII_ppb'][0], 0.1) np.testing.assert_allclose(output['gas_H2O2_ppb'], 0.5) np.testing.assert_allclose(output['gas_N_V_ppb'], 0.1) np.testing.assert_allclose(output['gas_O3_ppb'], 50) np.testing.assert_allclose(output['gas_C_IV_ppb'], 360 * 1000) rtol = 0.15 mass_conc_SO4mm = 2 mass_conc_NH4p = 0.375 num_conc_SO4mm = mass_conc_SO4mm / Substance.from_formula( "SO4").mass * si.gram / si.mole num_conc_NH4p = mass_conc_NH4p / Substance.from_formula( "NH4").mass * si.gram / si.mole np.testing.assert_allclose(num_conc_NH4p, num_conc_SO4mm, rtol=.005) mass_conc_H = num_conc_NH4p * Substance.from_formula( "H").mass * si.gram / si.mole np.testing.assert_allclose( actual=np.asarray(output['q_dry']) * np.asarray(output['rhod']), desired=mass_conc_NH4p + mass_conc_SO4mm + mass_conc_H, rtol=rtol) expected = {k: 0 for k in AQUEOUS_COMPOUNDS} expected['S_VI'] = mass_conc_SO4mm * si.ug / si.m**3 expected['N_mIII'] = mass_conc_NH4p * si.ug / si.m**3 for key in expected: mole_fraction = np.asarray(output[f"aq_{key}_ppb"]) convert_to(mole_fraction, 1 / PPB) compound = AQUEOUS_COMPOUNDS[key][0] # sic! np.testing.assert_allclose( actual=(settings.formulae.trivia.mole_fraction_2_mixing_ratio( mole_fraction, specific_gravity=specific_gravities[compound]) * np.asarray(output['rhod'])), desired=expected[key], rtol=rtol)
def example_output(): settings = Settings(n_sd=16, dt=1 * si.s, n_substep=5) simulation = Simulation(settings) output = simulation.run() return output
def test_calc_ionic_strength(nt, n_sd): formulae = Formulae() const = formulae.constants EQUILIBRIUM_CONST = EquilibriumConsts(formulae).EQUILIBRIUM_CONST K = _K(NH3=EQUILIBRIUM_CONST["K_NH3"].at(const.ROOM_TEMP), SO2=EQUILIBRIUM_CONST["K_SO2"].at(const.ROOM_TEMP), HSO3=EQUILIBRIUM_CONST["K_HSO3"].at(const.ROOM_TEMP), HSO4=EQUILIBRIUM_CONST["K_HSO4"].at(const.ROOM_TEMP), HCO3=EQUILIBRIUM_CONST["K_HCO3"].at(const.ROOM_TEMP), CO2=EQUILIBRIUM_CONST["K_CO2"].at(const.ROOM_TEMP), HNO3=EQUILIBRIUM_CONST["K_HNO3"].at(const.ROOM_TEMP)) settings = Settings(dt=1, n_sd=n_sd, n_substep=5) settings.t_max = nt * settings.dt simulation = Simulation(settings) simulation.run() H = simulation.particulator.attributes['conc_N_mIII'].data conc = { 'H+': H, 'N-3': simulation.particulator.attributes['conc_N_mIII'].data, 'N+5': simulation.particulator.attributes['conc_N_V'].data, 'S+4': simulation.particulator.attributes['conc_S_IV'].data, 'S+6': simulation.particulator.attributes['conc_S_VI'].data, 'C+4': simulation.particulator.attributes['conc_C_IV'].data, } alpha_C = (1 + K.CO2 / conc['H+'] + K.CO2 * K.HCO3 / conc['H+']**2) alpha_S = (1 + K.SO2 / conc['H+'] + K.SO2 * K.HSO3 / conc['H+']**2) alpha_N3 = (1 + conc['H+'] * K.NH3 / K_H2O) alpha_N5 = (1 + K.HNO3 / conc['H+']) actual = calc_ionic_strength(H=conc['H+'], conc=_conc( N_mIII=conc['N-3'], N_V=conc['N+5'], C_IV=conc['C+4'], S_IV=conc['S+4'], S_VI=conc['S+6'], ), K=K) expected = ionic_strength( { 'H+': conc['H+'] / const.rho_w, 'HCO3-': K.CO2 / conc['H+'] * conc['C+4'] / alpha_C / const.rho_w, 'CO3-2': K.CO2 / conc['H+'] * K.HCO3 / conc['H+'] * conc['C+4'] / alpha_C / const.rho_w, 'HSO3-': K.SO2 / conc['H+'] * conc['S+4'] / alpha_S / const.rho_w, 'SO3-2': K.SO2 / conc['H+'] * K.HSO3 / conc['H+'] * conc['S+4'] / alpha_S / const.rho_w, 'NH4+': K.NH3 / K_H2O * conc['H+'] * conc['N-3'] / alpha_N3 / const.rho_w, 'NO3-': K.HNO3 / conc['H+'] * conc['N+5'] / alpha_N5 / const.rho_w, 'HSO4-': conc['H+'] * conc['S+6'] / (conc['H+'] + K.HSO4) / const.rho_w, 'SO4-2': K.HSO4 * conc['S+6'] / (conc['H+'] + K.HSO4) / const.rho_w, 'OH-': K_H2O / conc['H+'] / const.rho_w }, warn=False) * const.rho_w np.testing.assert_allclose(actual, expected, rtol=1e-15)
def test_calc_ionic_strength(nt, n_sd): formulae = Formulae() EQUILIBRIUM_CONST = EquilibriumConsts(formulae).EQUILIBRIUM_CONST K_NH3 = EQUILIBRIUM_CONST["K_NH3"].at(ROOM_TEMP) K_SO2 = EQUILIBRIUM_CONST["K_SO2"].at(ROOM_TEMP) K_HSO3 = EQUILIBRIUM_CONST["K_HSO3"].at(ROOM_TEMP) K_HSO4 = EQUILIBRIUM_CONST["K_HSO4"].at(ROOM_TEMP) K_HCO3 = EQUILIBRIUM_CONST["K_HCO3"].at(ROOM_TEMP) K_CO2 = EQUILIBRIUM_CONST["K_CO2"].at(ROOM_TEMP) K_HNO3 = EQUILIBRIUM_CONST["K_HNO3"].at(ROOM_TEMP) settings = Settings(dt=1, n_sd=n_sd, n_substep=5) settings.t_max = nt * settings.dt simulation = Simulation(settings) simulation.run() H = simulation.core.particles['conc_N_mIII'].data conc = { 'H+': H, 'N-3': simulation.core.particles['conc_N_mIII'].data, 'N+5': simulation.core.particles['conc_N_V'].data, 'S+4': simulation.core.particles['conc_S_IV'].data, 'S+6': simulation.core.particles['conc_S_VI'].data, 'C+4': simulation.core.particles['conc_C_IV'].data, } alpha_C = (1 + K_CO2 / conc['H+'] + K_CO2 * K_HCO3 / conc['H+']**2) alpha_S = (1 + K_SO2 / conc['H+'] + K_SO2 * K_HSO3 / conc['H+']**2) alpha_N3 = (1 + conc['H+'] * K_NH3 / K_H2O) alpha_N5 = (1 + K_HNO3 / conc['H+']) actual = calc_ionic_strength(H=conc['H+'], N_mIII=conc['N-3'], N_V=conc['N+5'], C_IV=conc['C+4'], S_IV=conc['S+4'], S_VI=conc['S+6'], K_NH3=K_NH3, K_SO2=K_SO2, K_HSO3=K_HSO3, K_HSO4=K_HSO4, K_HCO3=K_HCO3, K_CO2=K_CO2, K_HNO3=K_HNO3) expected = ionic_strength( { 'H+': conc['H+'] / rho_w, 'HCO3-': K_CO2 / conc['H+'] * conc['C+4'] / alpha_C / rho_w, 'CO3-2': K_CO2 / conc['H+'] * K_HCO3 / conc['H+'] * conc['C+4'] / alpha_C / rho_w, 'HSO3-': K_SO2 / conc['H+'] * conc['S+4'] / alpha_S / rho_w, 'SO3-2': K_SO2 / conc['H+'] * K_HSO3 / conc['H+'] * conc['S+4'] / alpha_S / rho_w, 'NH4+': K_NH3 / K_H2O * conc['H+'] * conc['N-3'] / alpha_N3 / rho_w, 'NO3-': K_HNO3 / conc['H+'] * conc['N+5'] / alpha_N5 / rho_w, 'HSO4-': conc['H+'] * conc['S+6'] / (conc['H+'] + K_HSO4) / rho_w, 'SO4-2': K_HSO4 * conc['S+6'] / (conc['H+'] + K_HSO4) / rho_w, 'OH-': K_H2O / conc['H+'] / rho_w }, warn=False) * rho_w np.testing.assert_allclose(actual, expected, rtol=1e-15)
def test_calc_ionic_strength(nt, n_sd): from chempy.electrolytes import ionic_strength from PySDM_examples.Kreidenweis_et_al_2003 import Settings, Simulation from PySDM.backends.numba.impl._chemistry_methods import calc_ionic_strength from PySDM.physics.constants import rho_w, ROOM_TEMP K_NH3 = EQUILIBRIUM_CONST["K_NH3"].at(ROOM_TEMP) K_SO2 = EQUILIBRIUM_CONST["K_SO2"].at(ROOM_TEMP) K_HSO3 = EQUILIBRIUM_CONST["K_HSO3"].at(ROOM_TEMP) K_HSO4 = EQUILIBRIUM_CONST["K_HSO4"].at(ROOM_TEMP) K_HCO3 = EQUILIBRIUM_CONST["K_HCO3"].at(ROOM_TEMP) K_CO2 = EQUILIBRIUM_CONST["K_CO2"].at(ROOM_TEMP) K_HNO3 = EQUILIBRIUM_CONST["K_HNO3"].at(ROOM_TEMP) settings = Settings(dt=1, n_sd=n_sd, n_substep=5) settings.t_max = nt * settings.dt simulation = Simulation(settings) simulation.run() H = simulation.core.particles['conc_N_mIII'].data conc = { 'H+': H, 'N-3': simulation.core.particles['conc_N_mIII'].data, 'N+5': simulation.core.particles['conc_N_V'].data, 'S+4': simulation.core.particles['conc_S_IV'].data, 'S+6': simulation.core.particles['conc_S_VI'].data, 'C+4': simulation.core.particles['conc_C_IV'].data, } alpha_C = (1 + K_CO2 / conc['H+'] + K_CO2 * K_HCO3 / conc['H+']**2) alpha_S = (1 + K_SO2 / conc['H+'] + K_SO2 * K_HSO3 / conc['H+']**2) alpha_N3 = (1 + conc['H+'] * K_NH3 / K_H2O) alpha_N5 = (1 + K_HNO3 / conc['H+']) actual = calc_ionic_strength(H=conc['H+'], N_mIII=conc['N-3'], N_V=conc['N+5'], C_IV=conc['C+4'], S_IV=conc['S+4'], S_VI=conc['S+6'], K_NH3=K_NH3, K_SO2=K_SO2, K_HSO3=K_HSO3, K_HSO4=K_HSO4, K_HCO3=K_HCO3, K_CO2=K_CO2, K_HNO3=K_HNO3) expected = ionic_strength( { 'H+': conc['H+'] / rho_w, 'HCO3-': K_CO2 / conc['H+'] * conc['C+4'] / alpha_C / rho_w, 'CO3-2': K_CO2 / conc['H+'] * K_HCO3 / conc['H+'] * conc['C+4'] / alpha_C / rho_w, 'HSO3-': K_SO2 / conc['H+'] * conc['S+4'] / alpha_S / rho_w, 'SO3-2': K_SO2 / conc['H+'] * K_HSO3 / conc['H+'] * conc['S+4'] / alpha_S / rho_w, 'NH4+': K_NH3 / K_H2O * conc['H+'] * conc['N-3'] / alpha_N3 / rho_w, 'NO3-': K_HNO3 / conc['H+'] * conc['N+5'] / alpha_N5 / rho_w, 'HSO4-': conc['H+'] * conc['S+6'] / (conc['H+'] + K_HSO4) / rho_w, 'SO4-2': K_HSO4 * conc['S+6'] / (conc['H+'] + K_HSO4) / rho_w, 'OH-': K_H2O / conc['H+'] / rho_w }, warn=False) * rho_w np.testing.assert_allclose(actual, expected, rtol=1e-15)