def test_thd_initialisation(settings_idx): setup = setups[settings_idx] pv0 = setup.p0 / (1 + CONST.eps / setup.q0) pd0 = setup.p0 - pv0 phys = Formulae().trivia thd0 = phys.th_std(pd0, setup.T0) TestInitialisation.simulation_test('thd', thd0, setup)
def test_latent_heats(plot=False): # Arrange formulae = {k: Formulae(latent_heat=k) for k in ('Kirchhoff', 'Lowe2019')} temperature = np.linspace(-20, 20) + const.T_tri # Plot pyplot.axhline(const.l_tri, label='triple point', color='red') pyplot.axvline(const.T_tri, color='red') for key, val in formulae.items(): for name, func in inspect.getmembers(val.latent_heat): if name[:2] not in ('__', 'a_'): pyplot.plot(temperature, func(temperature), label=f"{key}::{name}") pyplot.grid() pyplot.legend() pyplot.xlabel('T [K]') pyplot.ylabel('Lv [J/kg]') if plot: pyplot.show() # Assert temperature = np.linspace(-20, 20, 100) + const.T_tri np.testing.assert_allclose( Formulae(latent_heat='Kirchhoff').latent_heat.lv(temperature), Formulae(latent_heat='Lowe2019').latent_heat.lv(temperature), rtol=1e-2)
def test_moments_range(backend_class, min_x, max_x, value, expected): # Arrange backend = backend_class(Formulae()) arr = lambda x: backend.Storage.from_ndarray(np.asarray((x, ))) moment_0 = arr(0.) moments = backend.Storage.from_ndarray(np.full((1, 1), 0.)) kw_args = { 'multiplicity': arr(1), 'attr_data': arr(0), 'cell_id': arr(0), 'idx': arr(0), 'length': 1, 'ranks': arr(0), 'x_attr': arr(value), 'weighting_attribute': arr(0), 'weighting_rank': 0, 'skip_division_by_m0': False } # Act backend.moments(moment_0=moment_0, moments=moments, min_x=min_x, max_x=max_x, **kw_args) # Assert assert moment_0.to_ndarray()[:] == moments.to_ndarray()[:] == expected
def test_koehler_maxima(constants, aerosol, surface_tension, maximum_x, maximum_y, bimodal): # arrange label = { 'CompressedFilmOvadnevaite': 'film', 'Constant': 'bulk' }[surface_tension] formulae = Formulae(surface_tension=surface_tension, constants={ 'sgm_org': 40 * si.mN / si.m, 'delta_min': 0.1 * si.nm }) sigma = formulae.surface_tension.sigma( np.nan, V_WET, V_DRY, aerosol.aerosol_modes[0]['f_org']) RH_eq = formulae.hygroscopicity.RH_eq( R_WET, TEMPERATURE, aerosol.aerosol_modes[0]['kappa'][label], R_DRY**3, sigma) # act peaks = signal.find_peaks(RH_eq) # assert assert np.argmax(RH_eq) == (np.abs(R_WET - maximum_x)).argmin() np.testing.assert_approx_equal((np.amax(RH_eq) - 1) * 100, maximum_y, significant=3) assert len(peaks) == 2 if bimodal else 1
def __init__( self, w_avg: float, N_STP: float, r_dry: float, mass_of_dry_air: float, coord: str = "VolumeLogarithm", ): self.formulae = Formulae( saturation_vapour_pressure="AugustRocheMagnus", condensation_coordinate=coord, ) const = self.formulae.constants self.p0 = 1000 * si.hectopascals self.RH0 = 0.98 self.kappa = 0.2 # TODO #441 self.T0 = 300 * si.kelvin self.z_half = 150 * si.metres pvs = self.formulae.saturation_vapour_pressure.pvs_Celsius(self.T0 - const.T0) self.q0 = const.eps / (self.p0 / self.RH0 / pvs - 1) self.w_avg = w_avg self.r_dry = r_dry self.N_STP = N_STP self.n_in_dv = N_STP / const.rho_STP * mass_of_dry_air self.mass_of_dry_air = mass_of_dry_air self.n_output = 500 self.rtol_x = condensation.DEFAULTS.rtol_x self.rtol_thd = condensation.DEFAULTS.rtol_thd self.coord = "volume logarithm" self.dt_cond_range = condensation.DEFAULTS.cond_range
def __init__( self, dz: float, n_sd_per_mode: int, aerosol: DryAerosolMixture, model: str, spectral_sampling: type(spec_sampling.SpectralSampling), w: float = 0.32 * si.m / si.s, delta_min: float = 0.1, # 0.2 in paper, but 0.1 matches plot fig 1c/d MAC: float = 1, HAC: float = 1, c_pd: float = 1005 * si.joule / si.kilogram / si.kelvin, g_std: float = sci.g * si.metre / si.second**2, BDF: bool = False, ): assert model in ("Constant", "CompressedFilmOvadnevaite") self.model = model self.n_sd_per_mode = n_sd_per_mode self.BDF = BDF self.formulae = Formulae( surface_tension=model, constants={ "sgm_org": 40 * si.mN / si.m, "delta_min": delta_min * si.nm, "MAC": MAC, "HAC": HAC, "c_pd": c_pd, "g_std": g_std, }, diffusion_kinetics="LoweEtAl2019", diffusion_thermics="LoweEtAl2019", latent_heat="Lowe2019", saturation_vapour_pressure="Lowe1977", ) const = self.formulae.constants self.aerosol = aerosol self.spectral_sampling = spectral_sampling max_altitude = 200 * si.m self.w = w self.t_max = max_altitude / self.w self.dt = dz / self.w self.output_interval = 1 * self.dt self.g = 9.81 * si.m / si.s**2 self.p0 = 980 * si.mbar self.T0 = 280 * si.K pv0 = 0.99 * self.formulae.saturation_vapour_pressure.pvs_Celsius( self.T0 - const.T0) self.q0 = const.eps * pv0 / (self.p0 - pv0) self.cloud_radius_range = (0.5 * si.micrometre, np.inf) self.mass_of_dry_air = 44 self.wet_radius_bins_edges = np.logspace(np.log10(4 * si.um), np.log10(12 * si.um), 128 + 1, endpoint=True)
def get_displacement(self, backend, scheme): formulae = Formulae(particle_advection=scheme) particulator = DummyParticulator(backend, n_sd=len(self.n), formulae=formulae) particulator.environment = DummyEnvironment( timestep=self.dt, grid=self.grid, courant_field_data=self.courant_field_data) positions = np.array(self.positions) cell_id, cell_origin, position_in_cell = particulator.mesh.cellular_attributes( positions) attributes = { 'n': self.n, 'volume': self.volume, 'cell id': cell_id, 'cell origin': cell_origin, 'position in cell': position_in_cell } particulator.build(attributes) sut = Displacement(enable_sedimentation=self.sedimentation) sut.register(particulator) sut.upload_courant_field(self.courant_field_data) return sut, particulator
def __init__(self, steps: Optional[list] = None): steps = steps or [200 * i for i in range(10)] self.formulae = Formulae() self.init_x_min = self.formulae.trivia.volume(radius=3.94 * si.micrometre) self.init_x_max = self.formulae.trivia.volume(radius=25 * si.micrometres) self.n_sd = 2**13 self.n_part = 239 / si.cm**3 self.X0 = self.formulae.trivia.volume(radius=10 * si.micrometres) self.dv = ( 1e1 * si.metres**3 ) # 1e6 -> overflows on ThrustRTC (32-bit int multiplicities) self.norm_factor = self.n_part * self.dv self.rho = 1000 * si.kilogram / si.metre**3 self.dt = 1 * si.seconds self.adaptive = False self.seed = 44 self._steps = steps self.kernel = collision_kernels.Geometric(collection_efficiency=1) self.spectrum = spectra.Exponential(norm_factor=self.norm_factor, scale=self.X0) # Note 220 instead of 200 for smoothing self.radius_bins_edges = np.logspace(np.log10(3.94 * si.um), np.log10(220 * si.um), num=100, endpoint=True)
def test_bulk_surface_tension_is_sgm_w(): # arrange formulae = Formulae(surface_tension='Constant') # act sigma = formulae.surface_tension.sigma(np.nan, V_WET, np.nan, np.nan) # assert assert sigma == const.sgm_w
def make_particulator( *, constants, n_sd, dt, initial_temperature, singular, seed, shima_T_fz, ABIFM_spec, droplet_volume, total_particle_number, volume ): attributes = {"volume": np.ones(n_sd) * droplet_volume} formulae_ctor_args = {"seed": seed, "constants": constants} if singular: formulae_ctor_args["freezing_temperature_spectrum"] = shima_T_fz else: formulae_ctor_args["heterogeneous_ice_nucleation_rate"] = "ABIFM" formulae = Formulae(**formulae_ctor_args) if singular: sampling = SpectroGlacialSampling( freezing_temperature_spectrum=formulae.freezing_temperature_spectrum, insoluble_surface_spectrum=ABIFM_spec, seed=formulae.seed, ) attributes["freezing temperature"], _, attributes["n"] = sampling.sample(n_sd) else: sampling = ConstantMultiplicity( spectrum=ABIFM_spec, # seed=formulae.seed ) attributes["immersed surface area"], attributes["n"] = sampling.sample(n_sd) attributes["n"] *= total_particle_number builder = Builder(n_sd, CPU(formulae)) env = Box(dt, volume) builder.set_environment(env) env["T"] = initial_temperature env["RH"] = A_VALUE_LARGER_THAN_ONE builder.add_dynamic(Freezing(singular=singular)) return builder.build( attributes=attributes, products=( PySDM_products.Time(name="t"), PySDM_products.AmbientTemperature(name="T"), PySDM_products.SpecificIceWaterContent(name="qi"), ), )
def __init__(self, settings, backend=CPU): dt_output = (settings.total_time / settings.n_steps ) # TODO #334 overwritten in notebook self.n_substeps = 1 # TODO #334 use condensation substeps while dt_output / self.n_substeps >= settings.dt_max: self.n_substeps += 1 self.formulae = Formulae( condensation_coordinate=settings.coord, saturation_vapour_pressure="AugustRocheMagnus", ) self.bins_edges = self.formulae.trivia.volume(settings.r_bins_edges) builder = Builder(backend=backend(formulae=self.formulae), n_sd=settings.n_sd) builder.set_environment( Parcel( dt=dt_output / self.n_substeps, mass_of_dry_air=settings.mass_of_dry_air, p0=settings.p0, q0=settings.q0, T0=settings.T0, w=settings.w, z0=settings.z0, )) environment = builder.particulator.environment builder.add_dynamic(AmbientThermodynamics()) condensation = Condensation( adaptive=settings.adaptive, rtol_x=settings.rtol_x, rtol_thd=settings.rtol_thd, dt_cond_range=settings.dt_cond_range, ) builder.add_dynamic(condensation) products = [ PySDM_products.ParticleSizeSpectrumPerVolume( name="Particles Wet Size Spectrum", radius_bins_edges=settings.r_bins_edges, ), PySDM_products.CondensationTimestepMin(name="dt_cond_min"), PySDM_products.CondensationTimestepMax(name="dt_cond_max"), PySDM_products.RipeningRate(), ] attributes = environment.init_attributes(n_in_dv=settings.n, kappa=settings.kappa, r_dry=settings.r_dry) self.particulator = builder.build(attributes, products) self.n_steps = settings.n_steps
def test_bulk_surface_tension_is_sgm_w(): # arrange formulae = Formulae(surface_tension='Constant') r_wet = np.logspace(np.log(150 * si.nm), np.log(3000 * si.nm), base=np.e, num=100) v_wet = formulae.trivia.volume(r_wet) # act sigma = formulae.surface_tension.sigma(np.nan, v_wet, np.nan, np.nan) # assert assert sigma == const.sgm_w
def main(): settings = Settings(Formulae()) settings.n_sd_per_gridbox = 25 settings.grid = (25, 25) settings.simulation_time = 5400 * si.second storage = Storage() simulation = Simulation(settings, storage, SpinUp) simulation.reinit() simulation.run() temp_file = TemporaryFile(".nc") exporter = NetCDFExporter(storage, settings, simulation, temp_file.absolute_path) exporter.run(controller=DummyController())
def test_SL_surface_tension(): # arrange formulae = Formulae( surface_tension='SzyszkowskiLangmuir', constants={ 'RUEHL_nu_org': aer.aerosol_modes[0]['nu_org'], 'RUEHL_A0': 115e-20 * si.m * si.m, 'RUEHL_C0': 6e-7, 'RUEHL_sgm_min': 40.0 * si.mN / si.m } ) # act sigma = formulae.surface_tension.sigma.py_func( TEMPERATURE, V_WET, V_DRY, aer.aerosol_modes[0]['f_org'] ) # assert test = np.array([ 0.04170545235068712,0.04207589632089122,0.04244962961646187,0.0428269485292672, 0.04320818372912586,0.043593705564928045,0.04398393042681806,0.044379328434762944, 0.04478043279935583,0.0451878513103144,0.04560228055915656,0.04602452371335611, 0.046455512957773316,0.046896338148322336,0.047348283850285854,0.04781287786790929, 0.048291955790905984,0.04878774828646771,0.049303001368475236,0.04984114559483559, 0.05040653975448352,0.05100483126948219,0.05164350541293351,0.05233275085500362, 0.0530868749743816,0.05392670762306413,0.05488381111799955,0.056007799064006464, 0.0573767511936752,0.059091334616161374,0.06113470871082447,0.0630992101730827, 0.06461657500220883,0.06572817193513107,0.06656632645301724,0.067223028931838, 0.06775426559459131,0.0681947966802138,0.06856720046770072,0.06888681108136241, 0.06916445633257737,0.06940803880572587,0.06962348937406575,0.06981536583363962, 0.06998724243835744,0.07014197148608561,0.07028186391110573,0.07040881703533021, 0.07052440690758358,0.0706299563370761,0.07072658588354233,0.07081525266495645, 0.07089678030539454,0.0709718823377164,0.07104118070169195,0.07110522051890375, 0.07116448200738978,0.07121939017482383,0.07127032276888652,0.07131761684754595, 0.07136157424698915,0.07140246616194773,0.07144053700595183,0.07147600768332182, 0.0715090783774165,0.07153993093863077,0.07156873093930102,0.07159562944989181, 0.07162076458075536,0.07164426282575462,0.07166624023764522,0.07168680345997547, 0.07170605063610594,0.0717240722125759,0.07174095165128262,0.071756766062675, 0.07177158677029272,0.0717854798154341,0.07179850640944603,0.07181072334005235, 0.0718221833372332,0.07183293540340689,0.07184302511202303,0.07185249487812974, 0.07186138420401451,0.07186972990262169,0.07187756630111092,0.0718849254266297, 0.07189183717612246,0.07189832947178237,0.07190442840356441,0.07191015836001612, 0.07191554214854026,0.07192060110608092,0.07192535520111608,0.07192982312774573, 0.0719340223925809,0.07193796939506661,0.07194167950180663,0.07194516711540175 ]) assert np.allclose(sigma, test, atol=1e-8)
def __init__( self, formulae=None, rhod_w_max: float = 0.6 * si.metres / si.seconds * (si.kilogram / si.metre**3), ): super().__init__(formulae or Formulae(), rhod_w_max=rhod_w_max) self.grid = (25, 25) self.size = (1500 * si.metres, 1500 * si.metres) # output steps self.simulation_time = 90 * si.minute self.dt = 5 * si.second self.spin_up_time = 1 * si.hour
def test_kink_location(constants, aerosol, cutoff): # arrange formulae = Formulae(surface_tension='CompressedFilmOvadnevaite', constants={ 'sgm_org': 40 * si.mN / si.m, 'delta_min': 0.1 * si.nm }) # act sigma = formulae.surface_tension.sigma( np.nan, V_WET, V_DRY, aerosol.aerosol_modes[0]['f_org']) # assert cutoff_idx = (np.abs(R_WET - cutoff)).argmin() assert (sigma[:cutoff_idx] == formulae.constants.sgm_org).all() assert sigma[cutoff_idx] > formulae.constants.sgm_org assert .98 * const.sgm_w < sigma[-1] <= const.sgm_w
def test_saturation_vapour_pressures(plot=False): # Arrange choices = _choices(saturation_vapour_pressure) formulae = {k: Formulae(saturation_vapour_pressure=k) for k in choices} temperature = np.linspace(-.2, .4) # Plot pyplot.axhline(const.p_tri, label='triple point', color='red') pyplot.axvline(const.T_tri - const.T0, color='red') for key, val in formulae.items(): for name, func in inspect.getmembers(val.saturation_vapour_pressure): if name[:2] not in ('__', 'a_'): if not (key == "AugustRocheMagnus" and name == "ice_Celsius"): pyplot.plot(temperature, func(temperature), label=f"{key}::{name}") pyplot.grid() pyplot.legend() pyplot.xlabel('T [C]') pyplot.ylabel('p [Pa]') if plot: pyplot.show() # Assert temperature = np.linspace(-20, 20, 100) choices_keys = tuple(choices.keys()) for choice in choices_keys[1:]: np.testing.assert_allclose( Formulae(saturation_vapour_pressure=choices_keys[0] ).saturation_vapour_pressure.pvs_Celsius(temperature), Formulae(saturation_vapour_pressure=choice ).saturation_vapour_pressure.pvs_Celsius(temperature), rtol=2e-2) for choice in choices_keys[1:]: if choice != 'AugustRocheMagnus': temperature = np.linspace(-20, 0, 100) np.testing.assert_array_less( Formulae(saturation_vapour_pressure=choices_keys[0] ).saturation_vapour_pressure.ice_Celsius(temperature), Formulae(saturation_vapour_pressure=choice). saturation_vapour_pressure.pvs_Celsius(temperature)) temperature = np.linspace(1, 1, 100) np.testing.assert_array_less( Formulae(saturation_vapour_pressure=choices_keys[0] ).saturation_vapour_pressure.pvs_Celsius(temperature), Formulae(saturation_vapour_pressure=choice). saturation_vapour_pressure.ice_Celsius(temperature))
def test_accomodation_coefficients(constants): # arrange formulae = Formulae(constants=constants) D = 1 K = 2 r = 3 lmbd = 4 # act D_dk = formulae.diffusion_kinetics.D(D, r, lmbd) K_dk = formulae.diffusion_kinetics.K(K, r, lmbd) # assert Kn = lmbd / r xx_D = 4 / 3 / constants['MAC'] np.testing.assert_almost_equal(D_dk, D * (1 + Kn) / (1 + (xx_D + .377) * Kn + xx_D * Kn**2)) xx_K = 4 / 3 / constants['HAC'] np.testing.assert_almost_equal(K_dk, K * (1 + Kn) / (1 + (xx_K + .377) * Kn + xx_K * Kn**2))
def test_freezing(singular): # Arrange settings = Settings( Formulae(seed=44, condensation_coordinate='VolumeLogarithm', fastmath=True, freezing_temperature_spectrum='Niemand_et_al_2012', heterogeneous_ice_nucleation_rate='ABIFM', constants={ 'NIEMAND_A': -0.517, 'NIEMAND_B': 8.934, 'ABIFM_M': 28.13797, 'ABIFM_C': -2.92414 })) settings.dt = .5 * si.second settings.grid = (5, 15) settings.n_sd_per_gridbox = 64 settings.simulation_time = 100 * settings.dt settings.spin_up_time = 10 * settings.dt settings.output_interval = settings.dt # settings.simulation_time settings.processes['freezing'] = True settings.processes['coalescence'] = False settings.freezing_singular = singular settings.th_std0 -= 35 * si.K settings.qv0 -= 7.15 * si.g / si.kg storage = DummyStorage() simulation = Simulation(settings, storage, SpinUp=SpinUp, backend_class=CPU) simulation.reinit() # Act simulation.run() # Assert assert (simulation.products['ice water content'].get() > 0).any()
def __init__(self, steps: Optional[list] = None): steps = steps or [0, 1200, 2400, 3600] self.formulae = Formulae() self.n_sd = 2**13 self.n_part = 2**23 / si.metre**3 self.X0 = self.formulae.trivia.volume(radius=30.531 * si.micrometres) self.dv = 1e6 * si.metres**3 self.norm_factor = self.n_part * self.dv self.rho = 1000 * si.kilogram / si.metre**3 self.dt = 1 * si.seconds self.adaptive = False self.seed = 44 self.steps = steps self.kernel = Golovin(b=1.5e3 / si.second) self.spectrum = spectra.Exponential(norm_factor=self.norm_factor, scale=self.X0) self.radius_bins_edges = np.logspace(np.log10(10 * si.um), np.log10(5e3 * si.um), num=128, endpoint=True)
def test_ovad_surface_tension(): # arrange formulae = Formulae( surface_tension='CompressedFilmOvadnevaite', constants={ 'sgm_org': 40 * si.mN / si.m, 'delta_min': 0.1 * si.nm } ) # act sigma = formulae.surface_tension.sigma.py_func( np.nan, V_WET, V_DRY, aer.aerosol_modes[0]['f_org'] ) # assert test = np.array([ 0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04, 0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04, 0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04, 0.04,0.04,0.04,0.04,0.04,0.04145185206190185,0.043245940200403225, 0.04493465757799464,0.04652419321971498,0.04802037260212956, 0.049428679011056784,0.05075427364458418,0.05200201453477719, 0.053176474357604905,0.054281957196414696,0.055322514320391286, 0.056301959036044666,0.05722388066615342,0.05809165770721892, 0.05890847021403533,0.059677311456651734,0.06040099889241329, 0.06108218449344455,0.061723364467315724,0.06232688840684634, 0.06289496790207844,0.06342968464658641,0.06393299806742464,0.06440675250688739, 0.06485268398238773,0.06527242654921248,0.06566751828951929,0.06603940694949924, 0.06638945524541608,0.06671894585794522,0.06702908613316425,0.06732101250732575, 0.0675957946718376,0.06785443949346634,0.06809789470435344,0.06832705237523176, 0.06854275218466818,0.06874578449624996,0.06893689325503628,0.06911677871385602, 0.06928609999950004,0.06944547752817445,0.06959549527907691,0.06973670293439409, 0.06986961789368958,0.06999472716988156,0.0701124891739247,0.07022333539465174, 0.07032767197991878,0.07042588122494463,0.07051832297317082,0.07060533593488776, 0.0706872389283801,0.07076433204821643,0.07083689776487583,0.0709052019598351 ]) assert np.allclose(sigma, test, atol=1e-8)
def test_Arabas_et_al_2015_export(): # Arrange settings = GUISettings(Settings(Formulae())) settings.ui_nz.value += 1 settings.ui_simulation_time = IntSlider(value=10) settings.ui_dt = IntSlider(value=10) settings.ui_output_options["interval"] = IntSlider( value=settings.ui_dt.value) assert settings.n_steps == 1 assert len(settings.output_steps) == 2 and settings.output_steps[-1] == 1 storage = Storage() simulator = Simulation(settings=settings, storage=storage, SpinUp=SpinUp, backend_class=CPU) file = TemporaryFile() ncdf_exporter = NetCDFExporter( storage=storage, settings=settings, simulator=simulator, filename=file.absolute_path, ) tempdir = TemporaryDirectory() vtk_exporter = VTKExporter(path=tempdir.name) # Act simulator.reinit() simulator.run(vtk_exporter=vtk_exporter) ncdf_exporter.run(controller=DummyController()) vtk_exporter.write_pvd() # Assert versions = netcdf.netcdf_file( # pylint: disable=no-member file.absolute_path).versions assert "PyMPDATA" in str(versions) filenames_list = os.listdir(os.path.join(tempdir.name, "output")) assert len(list(filter(lambda x: x.endswith(".pvd"), filenames_list))) == 2 assert len(list(filter(lambda x: x.endswith(".vts"), filenames_list))) == 2 assert len(list(filter(lambda x: x.endswith(".vtu"), filenames_list))) == 2
def test_supersaturation_and_temperature_profile(s_max, s_250m, T_250m): # arrange settings = Settings( dz = 1 * si.m, n_sd_per_mode = (5, 5), aerosol_modes_by_kappa = { .54: Lognormal( norm_factor=850 / si.cm ** 3, m_mode=15 * si.nm, s_geom=1.6 ), 1.2: Lognormal( norm_factor=10 / si.cm ** 3, m_mode=850 * si.nm, s_geom=1.2 ) }, vertical_velocity = 1.0 * si.m / si.s, initial_pressure = 775 * si.mbar, initial_temperature = 274 * si.K, initial_relative_humidity = .98, displacement = 250 * si.m, formulae = Formulae(constants={'MAC': .3}) ) simulation = Simulation(settings, products=( ParcelDisplacement( name='z'), PeakSupersaturation( name='S_max', unit='%'), AmbientTemperature( name='T'), )) # act output = simulation.run() # assert np.testing.assert_approx_equal(np.nanmax(output['products']['S_max']), s_max, significant=2) np.testing.assert_approx_equal(output['products']['S_max'][-1], s_250m, significant=2) np.testing.assert_approx_equal(output['products']['T'][-1], T_250m, significant=2)
def test_ruehl_surface_tension(): # arrange formulae = Formulae( surface_tension='CompressedFilmRuehl', constants={ 'RUEHL_nu_org': aer.aerosol_modes[0]['nu_org'], 'RUEHL_A0': 115e-20 * si.m * si.m, 'RUEHL_C0': 6e-7, 'RUEHL_m_sigma': 0.3e17 * si.J / si.m**2, 'RUEHL_sgm_min': 40.0 * si.mN / si.m } ) # act sigma = np.zeros(len(V_WET)) for i,vw in enumerate(V_WET): sigma[i] = formulae.surface_tension.sigma.py_func( TEMPERATURE, vw, V_DRY, aer.aerosol_modes[0]['f_org'] ) # assert test = np.array([ 0.04318990388863848,0.04355416769160859,0.0439421778272239,0.04435543913070772, 0.04479551672881958,0.04526402672640544,0.045762623781862244,0.04629298561514597, 0.04685679492085086,0.04745571976463471,0.048091394314671604,0.04876540264111237, 0.04947926918577529,0.05023446017823065,0.05103240053506651,0.051874510426880245, 0.052762264625474335,0.05369727599685779,0.054681402290397516,0.05571687298813886, 0.056806430668716615,0.057953479146781645,0.05916222830454722,0.06043782250688807, 0.06178643524956695,0.06321530723603938,0.06473269985853751,0.06634773509867063, 0.0680701033701799,0.06990965125952682,0.07187591399036536,0.072,0.072,0.072,0.072, 0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072, 0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072, 0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072, 0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072, 0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072,0.072 ]) assert np.allclose(sigma, test, atol=1e-8)
def main(): settings = Settings(Formulae()) settings.grid = (25, 25) settings.simulation_time = settings.dt * 100 settings.output_interval = settings.dt * 10 settings.processes = { "particle advection": True, "fluid advection": True, "coalescence": True, "condensation": False, "sedimentation": True, "freezing": False, } n_sd = range(14, 16, 1) times = {} backends = [(CPU, "sync"), (CPU, "async")] if GPU.ENABLE: backends.append((GPU, "async")) for backend, mode in backends: if backend is CPU: PySDM.backends.impl_numba.conf.NUMBA_PARALLEL = mode reload_cpu_backend() key = f"{backend} (mode={mode})" times[key] = [] for sd in n_sd: settings.n_sd_per_gridbox = sd storage = Storage() simulation = Simulation(settings, storage, None, backend) simulation.reinit(products=[WallTime()]) simulation.run() times[key].append(storage.load("wall time")[-1]) for parallelization, t in times.items(): plt.plot(n_sd, t, label=parallelization) plt.legend() plt.loglog() plt.savefig("benchmark.pdf", format="pdf")
def test_freezing_temperature_spectra(model, plot=False): # Arrange formulae = Formulae(freezing_temperature_spectrum=model, constants={ 'NIEMAND_A': -0.517, 'NIEMAND_B': 8.934, 'BIGG_DT_MEDIAN': 33 }) temperature = np.linspace(const.T0, const.T0 - 40, num=100) # Act pdf = formulae.freezing_temperature_spectrum.pdf(temperature, A) cdf = formulae.freezing_temperature_spectrum.cdf(temperature, A) # Plot pylab.plot(temperature, pdf, linestyle='-', marker='o', label='pdf') pdfax = pylab.gca() cdfax = pdfax.twinx() cdfax.plot(temperature, cdf, linestyle='--', marker='x', label='cdf') pylab.xlabel('T [K]') pylab.xlim(np.amax(temperature), np.amin(temperature)) pdfax.set_ylabel('pdf [K$^{-1}$]') cdfax.set_ylabel('cdf [1]') pylab.grid() pylab.title(model) if plot: pylab.show() # Assert dT = abs(temperature[1] - temperature[0]) np.testing.assert_approx_equal(np.sum(pdf * dT), 1, significant=3) np.testing.assert_approx_equal(cdf[0] + 1, 1, significant=3) np.testing.assert_approx_equal(cdf[-1], 1, significant=3) if hasattr(formulae.freezing_temperature_spectrum, 'invcdf'): invcdf = formulae.freezing_temperature_spectrum.invcdf(cdf, A) np.testing.assert_allclose(invcdf, temperature)
def test_freeze_time_dependent(plot=False): # Arrange cases = ( {'dt': 5e5, 'N': 1}, {'dt': 1e6, 'N': 1}, {'dt': 5e5, 'N': 8}, {'dt': 1e6, 'N': 8}, {'dt': 5e5, 'N': 16}, {'dt': 1e6, 'N': 16}, ) rate = 1e-9 immersed_surface_area = 1 number_of_real_droplets = 1024 total_time = 2e9 # effectively interpretted here as seconds, i.e. cycle = 1 * si.s # dummy (but must-be-set) values vol = 44 # for sign flip (ice water has negative volumes), value does not matter d_v = 666 # products use conc., dividing there, multiplying here, value does not matter hgh = lambda t: np.exp(-0.8 * rate * (t - total_time / 10)) low = lambda t: np.exp(-1.2 * rate * (t + total_time / 10)) # Act output = {} for case in cases: n_sd = int(number_of_real_droplets // case['N']) assert n_sd == number_of_real_droplets / case['N'] assert total_time // case['dt'] == total_time / case['dt'] key = f"{case['dt']}:{case['N']}" output[key] = {'unfrozen_fraction': [], 'dt': case['dt'], 'N': case['N']} formulae = Formulae( heterogeneous_ice_nucleation_rate='Constant', constants={ 'J_HET': rate / immersed_surface_area } ) builder = Builder(n_sd=n_sd, backend=CPU(formulae=formulae)) env = Box(dt=case['dt'], dv=d_v) builder.set_environment(env) builder.add_dynamic(Freezing(singular=False)) attributes = { 'n': np.full(n_sd, int(case['N'])), 'immersed surface area': np.full(n_sd, immersed_surface_area), 'volume': np.full(n_sd, vol) } products = (IceWaterContent(name='qi'),) particulator = builder.build(attributes=attributes, products=products) env['a_w_ice'] = np.nan cell_id = 0 for i in range(int(total_time / case['dt']) + 1): particulator.run(0 if i == 0 else 1) ice_mass_per_volume = particulator.products['qi'].get()[cell_id] ice_mass = ice_mass_per_volume * d_v ice_number = ice_mass / (const.rho_w * vol) unfrozen_fraction = 1 - ice_number / number_of_real_droplets output[key]['unfrozen_fraction'].append(unfrozen_fraction)
def formulae(self) -> Formulae: return Formulae( **{widget.description: widget.value for widget in self.ui_formulae_options} )
def __init__( self, dt: float, n_sd: int, n_substep: int, spectral_sampling: spec_sampling.SpectralSampling = spec_sampling. Logarithmic, ): self.formulae = Formulae( saturation_vapour_pressure="AugustRocheMagnus", constants={"g_std": 10 * si.m / si.s**2}, ) const = self.formulae.constants self.DRY_RHO = 1800 * si.kg / (si.m**3) self.dry_molar_mass = Substance.from_formula( "NH4HSO4").mass * si.gram / si.mole self.system_type = "closed" self.t_max = (2400 + 196) * si.s self.output_interval = 10 * si.s self.dt = dt self.w = 0.5 * si.m / si.s self.n_sd = n_sd self.n_substep = n_substep self.p0 = 950 * si.mbar self.T0 = 285.2 * si.K pv0 = 0.95 * self.formulae.saturation_vapour_pressure.pvs_Celsius( self.T0 - T0) self.q0 = const.eps * pv0 / (self.p0 - pv0) self.kappa = 0.61 self.cloud_radius_range = (0.5 * si.micrometre, 25 * si.micrometre) self.mass_of_dry_air = 44 # note: rho is not specified in the paper rho0 = 1 self.r_dry, self.n_in_dv = spectral_sampling( spectrum=spectra.Lognormal( norm_factor=566 / si.cm**3 / rho0 * self.mass_of_dry_air, m_mode=0.08 * si.um / 2, s_geom=2, )).sample(n_sd) self.ENVIRONMENT_MOLE_FRACTIONS = { "SO2": 0.2 * PPB, "O3": 50 * PPB, "H2O2": 0.5 * PPB, "CO2": 360 * PPM, "HNO3": 0.1 * PPB, "NH3": 0.1 * PPB, } self.starting_amounts = { "moles_" + k: self.formulae.trivia.volume(self.r_dry) * self.DRY_RHO / self.dry_molar_mass if k in ("N_mIII", "S_VI") else np.zeros(self.n_sd) for k in AQUEOUS_COMPOUNDS } self.dry_radius_bins_edges = (np.logspace( np.log10(0.01 * si.um), np.log10(1 * si.um), 51, endpoint=True) / 2)
import numpy as np import pytest from PySDM.initialisation.sampling import spectral_sampling, spectro_glacial_sampling from PySDM.initialisation.spectra.lognormal import Lognormal from PySDM import Formulae m_mode = .5e-5 n_part = 256 * 16 s_geom = 1.5 spectrum = Lognormal(n_part, m_mode, s_geom) m_range = (.1e-6, 100e-6) formulae = Formulae( freezing_temperature_spectrum='Niemand_et_al_2012', constants={ 'NIEMAND_A': -0.517, 'NIEMAND_B': 8.934 } ) @pytest.mark.parametrize("discretisation", [ pytest.param(spectral_sampling.Linear(spectrum, m_range)), pytest.param(spectral_sampling.Logarithmic(spectrum, m_range)), pytest.param(spectral_sampling.ConstantMultiplicity(spectrum, m_range)), pytest.param(spectral_sampling.UniformRandom(spectrum, m_range)), # TODO #599 ]) def test_spectral_discretisation(discretisation): # Arrange n_sd = 100000