def test_event_rates(settings_idx): # Arrange settings = Settings(w_avg=setups[settings_idx].w_avg, N_STP=setups[settings_idx].N_STP, r_dry=setups[settings_idx].r_dry, mass_of_dry_air=1 * si.kg) const = settings.formulae.constants settings.n_output = 50 simulation = Simulation(settings) # Act output = simulation.run() # Assert rip = np.asarray(output['ripening_rate']) act = np.asarray(output['activating_rate']) dea = np.asarray(output['deactivating_rate']) act_max = np.full( (1, ), settings.n_in_dv / simulation.particulator.dt / const.rho_STP) convert_to(act_max, 1 / si.mg) assert (rip == 0).all() assert (act > 0).any() assert (dea > 0).any() assert 0 < max(act) < act_max[0] assert 0 < max(dea) < act_max[0]
def __init__(self, r_bins, initial_spectrum_per_mass_of_dry_air, show=True): super().__init__(*plt.subplots(1, 1)) self.ax.set_xlim(np.amin(r_bins), np.amax(r_bins)) self.ax.set_xlabel("particle radius [μm]") self.ax.set_ylabel( "specific concentration density [mg$^{-1}$ μm$^{-1}$]") self.ax.set_xscale("log") self.ax.set_yscale("log") self.ax.set_ylim(1, 5e3) self.ax.grid(True) vals = initial_spectrum_per_mass_of_dry_air.size_distribution( r_bins * const.si.um) const.convert_to(vals, const.si.mg**-1 / const.si.um) self.ax.plot(r_bins, vals, label="spectrum sampled at t=0") self.spec_wet = self.ax.step( r_bins, np.full_like(r_bins, np.nan), label="binned super-particle wet sizes", )[0] self.spec_dry = self.ax.step( r_bins, np.full_like(r_bins, np.nan), label="binned super-particle dry sizes", )[0] self.ax.legend() if show: plt.show()
def get(self): vals = np.empty( [self.particulator.mesh.n_cell, len(self.attr_bins_edges) - 1]) self.recalculate_spectrum_moment(attr=self.volume_attr, rank=1, filter_attr=self.volume_attr) for i in range(vals.shape[1]): self.download_spectrum_moment_to_buffer(rank=0, bin_number=i) vals[:, i] = self.buffer.ravel() if self.normalise_by_dv: vals[:] /= self.particulator.mesh.dv self.download_to_buffer(self.particulator.environment['rhod']) rhod = self.buffer.ravel() for i in range(len(self.attr_bins_edges) - 1): dr = self.formulae.trivia.radius(volume=self.attr_bins_edges[i + 1]) - \ self.formulae.trivia.radius(volume=self.attr_bins_edges[i]) vals[:, i] /= rhod * dr const.convert_to(vals, const.si.micrometre**-1 * const.si.milligram**-1) return np.squeeze(vals.reshape(self.shape))
def get(self): self.download_moment_to_buffer('volume', rank=0, filter_range=(self.formulae.trivia.volume(self.radius_range[0]), self.formulae.trivia.volume(self.radius_range[1]))) self.buffer[:] /= self.core.mesh.dv const.convert_to(self.buffer, const.si.centimetre**-3) return self.buffer
def get(self): self.download_moment_to_buffer( 'volume', rank=0, attr_range=[0, phys.volume(self.radius_threshold)]) self.buffer[:] /= self.particles.mesh.dv const.convert_to(self.buffer, const.si.centimetre**-3) return self.buffer
def get(self): self.download_moment_to_buffer('volume', rank=0) result = self.buffer.copy() # TODO #217 self.download_to_buffer(self.core.environment['rhod']) result[:] /= self.core.mesh.dv result[:] /= self.buffer const.convert_to(result, const.si.milligram**-1) return result
def get(self) -> float: if self.elapsed_time == 0.: return 0. result = rho_w * self.accumulated_rainfall / self.elapsed_time / (self.dv / self.dz) self._reset_counters() convert_to(result, si.mm / si.day) return result
def get(self): self.download_moment_to_buffer('volume', rank=0, filter_range=[0, phys.volume(self.radius_threshold)]) result = self.buffer.copy() # TODO self.download_to_buffer(self.core.environment['rhod']) result[:] /= self.core.mesh.dv result[:] /= self.buffer const.convert_to(result, const.si.milligram**-1) return result
def get(self): self.download_moment_to_buffer('dry volume', rank=1) self.buffer[:] *= self.density result = np.copy(self.buffer) self.download_moment_to_buffer('dry volume', rank=0) result[:] *= self.buffer self.download_to_buffer(self.core.environment['rhod']) result[:] /= self.core.mesh.dv result[:] /= self.buffer convert_to(result, si.ug / si.kg) return result
def get(self): tmp = np.empty_like(self.buffer) self.download_moment_to_buffer('volume', rank=2/3, filter_range=(self.formulae.trivia.volume(self.radius_range[0]), self.formulae.trivia.volume(self.radius_range[1]))) tmp[:] = self.buffer[:] self.download_moment_to_buffer('volume', rank=1, filter_range=(self.formulae.trivia.volume(self.radius_range[0]), self.formulae.trivia.volume(self.radius_range[1]))) CloudDropletEffectiveRadius.__get_impl(self.buffer, tmp) const.convert_to(self.buffer, const.si.micrometre) return self.buffer
def get(self): if self.timestep_count == 0: return self.event_count else: self.event_count[:] /= (self.timestep_count * self.particulator.dt * self.particulator.mesh.dv) self.download_to_buffer(self.particulator.environment['rhod']) self.event_count[:] /= self.buffer[:] self.buffer[:] = self.event_count[:] self.timestep_count = 0 self.event_count[:] = 0 convert_to(self.buffer, 1/si.mg) return self.buffer
def get(self): self.download_moment_to_buffer('volume', rank=0, filter_range=(self.formulae.trivia.volume(self.radius_range[0]), self.formulae.trivia.volume(self.radius_range[1]))) self.buffer[:] /= self.particulator.mesh.dv if self.specific: result = self.buffer.copy() # TODO #217 self.download_to_buffer(self.particulator.environment['rhod']) result[:] /= self.buffer else: result = self.buffer const.convert_to(result, const.si.mg**-1 if self.specific else const.si.centimetre**-3) return result
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 get(self): self.download_moment_to_buffer('volume', rank=0, filter_range=[ 0, self.formulae.trivia.volume( self.radius_threshold) ]) result = self.buffer.copy() # TODO #217 self.download_to_buffer(self.particulator.environment['rhod']) result[:] /= self.particulator.mesh.dv result[:] /= self.buffer const.convert_to(result, const.si.milligram**-1) return result
def reinit(self, products): self.products = products self.product_select.options = [(f"{val.description} [{val.unit}]", key) for key, val in products.items() if len(val.shape) == 2] r_bins = phys.radius(volume=self.setup.v_bins) const.convert_to(r_bins, const.si.micrometres) self.spectrumOutput = Output() with self.spectrumOutput: self.spectrumPlot = _SpectrumPlot(r_bins) clear_output() self.plots = {} self.outputs = {} for key, product in products.items(): if len(product.shape) == 2: self.outputs[key] = Output() with self.outputs[key]: self.plots[key] = _ImagePlot(self.setup.grid, self.setup.size, product) clear_output() self.plot_box = Box() if len(products.keys()) > 0: layout_flex_end = Layout(display='flex', justify_content='flex-end') save_map = Button(icon='save') save_map.on_click(self.handle_save_map) save_spe = Button(icon='save') save_spe.on_click(self.handle_save_spe) self.plots_box.children = (HBox(children=( VBox(children=( Box(layout=layout_flex_end, children=(save_map, )), HBox((self.slider['Z'], self.plot_box)), HBox((self.slider['X'], ), layout=layout_flex_end))), VBox(layout=Layout(), children=(save_spe, self.spectrumOutput)))), ) for widget in (self.step_slider, self.play): widget.value = 0 widget.max = len(self.setup.steps) - 1 for j, xz in enumerate(('X', 'Z')): slider = self.slider[xz] mx = self.setup.grid[j] slider.max = mx slider.value = (0, mx) self.replot()
def get(self): vals = np.empty([self.core.mesh.n_cell, len(self.attr_bins_edges) - 1]) self.recalculate_spectrum_moment(attr=f'moles_{self.key}', rank=1, filter_attr='dry volume') for i in range(vals.shape[1]): self.download_spectrum_moment_to_buffer(rank=1, bin_number=i) vals[:, i] = self.buffer.ravel() self.download_spectrum_moment_to_buffer(rank=0, bin_number=i) vals[:, i] *= self.buffer.ravel() vals *= self.molar_mass / np.diff( np.log10(2 * self.dry_radius_bins_edges)) / self.core.mesh.dv convert_to(vals, si.ug) if self.specific: self.download_to_buffer(self.core.environment['rhod']) vals[:] /= self.buffer return vals
def get(self): vals = np.empty([self.core.mesh.n_cell, len(self.v_bins) - 1]) for i in range(len(self.v_bins) - 1): self.download_moment_to_buffer( attr=self.volume_attr, rank=0, filter_attr=self.volume_attr, filter_range=(self.v_bins[i], self.v_bins[i + 1]) ) vals[:, i] = self.buffer.ravel() if self.normalise_by_dv: vals[:] /= self.core.mesh.dv self.download_to_buffer(self.core.environment['rhod']) rhod = self.buffer.ravel() for i in range(len(self.v_bins) - 1): dr = phys.radius(volume=self.v_bins[i + 1]) - phys.radius(volume=self.v_bins[i]) vals[:, i] /= rhod * dr const.convert_to(vals, const.si.micrometre**-1 * const.si.milligram**-1) return np.squeeze(vals.reshape(self.shape))
def get(self): vals = np.empty( [self.particulator.mesh.n_cell, len(self.attr_bins_edges) - 1]) self.recalculate_spectrum_moment(attr='volume', filter_attr='freezing temperature', rank=0) for i in range(vals.shape[1]): self.download_spectrum_moment_to_buffer(rank=0, bin_number=i) vals[:, i] = self.buffer.ravel() self.download_to_buffer(self.particulator.environment['rhod']) rhod = self.buffer.ravel() for i in range(len(self.attr_bins_edges) - 1): dT = abs(self.attr_bins_edges[i + 1] - self.attr_bins_edges[i]) vals[:, i] /= rhod * dT * self.particulator.mesh.dv const.convert_to(vals, const.si.milligram**-1 / const.si.K) return np.squeeze(vals.reshape(self.shape))
def get(self): volume_bins_edges = self.formulae.trivia.volume( self.dry_radius_bins_edges) vals = np.empty(len(volume_bins_edges) - 1) for i in range(len(vals)): self.download_moment_to_buffer( attr=f'moles_{self.key}', rank=1, filter_attr='dry volume', filter_range=(volume_bins_edges[i], volume_bins_edges[i + 1])) vals[i] = self.buffer[0] self.download_moment_to_buffer( attr='volume', rank=0, filter_attr='dry volume', filter_range=(volume_bins_edges[i], volume_bins_edges[i + 1])) vals[i] *= self.buffer[0] vals *= self.molar_mass / np.diff( np.log10(2 * self.dry_radius_bins_edges)) / self.core.mesh.dv convert_to(vals, si.ug) return vals
def plot(var, qlabel, fname, output, vmin=None, vmax=None, line=None): line = line or {15: ":", 20: "--", 25: "-", 30: "-."} dt = output["t"][1] - output["t"][0] dz = output["z"][1] - output["z"][0] tgrid = np.concatenate(((output["t"][0] - dt / 2, ), output["t"] + dt / 2)) zgrid = np.concatenate(((output["z"][0] - dz / 2, ), output["z"] + dz / 2)) convert_to(zgrid, si.km) fig = pyplot.figure(constrained_layout=True) gs = fig.add_gridspec(25, 5) ax1 = fig.add_subplot(gs[:-1, 0:4]) mesh = ax1.pcolormesh(tgrid, zgrid, output[var], cmap="twilight") ax1.set_xlabel("time [s]") ax1.set_ylabel("z [km]") ax1.set_ylim(0, None) cbar = fig.colorbar(mesh, fraction=0.05, location="top") cbar.set_label(qlabel) ax2 = fig.add_subplot(gs[:-1, 4:], sharey=ax1) ax2.set_xlabel(qlabel) # ax2.set_yticks([], minor=False) last_t = 0 for i, t in enumerate(output["t"]): x, z = output[var][:, i], output["z"].copy() convert_to(z, si.km) params = {"color": "black"} for line_t, line_s in line.items(): if last_t < line_t * si.min <= t: params["ls"] = line_s ax2.plot(x, z, **params) if vmin is not None and vmax is not None: ax1.axvline(t, ymin=vmin, ymax=vmax, **params) else: ax1.axvline(t, **params) last_t = t show_plot(filename=fname, inline_format="png")
def get(self, unit=const.si.micrometre): self.download_moment_to_buffer('volume', rank=1 / 3) self.buffer[:] /= const.pi_4_3**(1 / 3) const.convert_to(self.buffer, unit) return self.buffer
def get(self): self.download_to_buffer(self.environment['qv']) const.convert_to(self.buffer, const.si.gram / const.si.kilogram) return self.buffer
def reinit(self, products): self.products = products self.product_select.options = tuple( (f"{val.name} [{val.unit}]", key) for key, val in sorted(self.products.items(), key=lambda item: item[1].name) if len(val.shape) == 2) opts = [ ("particle size spectra", "size"), ("terminal velocity", "terminal velocity"), ] if "freezable specific concentration" in products: opts.append(("freezing temperature spectra", "temperature")) if "immersed surface area" in products: pass # TODO #599 self.spectrum_select.options = tuple(opts) r_bins = self.settings.r_bins_edges.copy() const.convert_to(r_bins, const.si.micrometres) self.spectrumOutputs = {} self.spectrumPlots = {} for key in ("size", "terminal velocity", "temperature"): self.spectrumOutputs[key] = Output() with self.spectrumOutputs[key]: self.spectrumPlots[key] = ( _SpectrumPlot(r_bins, self.settings.spectrum_per_mass_of_dry_air) if key == "size" else _TemperaturePlot( self.settings.T_bins_edges, self.settings.formulae) if key == "temperature" else _TerminalVelocityPlot( self.settings.terminal_velocity_radius_bin_edges, self.settings.formulae, )) clear_output() self.timeseriesOutput = Output() with self.timeseriesOutput: default_figsize = rcParams["figure.figsize"] fig_kw = { "figsize": (2.5 * default_figsize[0], default_figsize[1] / 2) } fig, ax = pyplot.subplots(1, 1, **fig_kw) self.timeseriesPlot = _TimeseriesPlot( fig, ax, self.settings.output_steps * self.settings.dt) clear_output() self.plots = {} self.outputs = {} for key, product in products.items(): if len(product.shape) == 2: self.outputs[key] = Output() with self.outputs[key]: fig, ax = pyplot.subplots(1, 1) self.plots[key] = _ImagePlot( fig, ax, self.settings.grid, self.settings.size, product, show=True, lines=True, ) clear_output() self.plot_box = Box() self.spectrum_box = Box() if len(products.keys()) > 0: layout_flex_end = Layout(display="flex", justify_content="flex-end") save_map = Button(icon="save") save_map.on_click(self.handle_save_map) save_spe = Button(icon="save") save_spe.on_click(self.handle_save_spe) self.plots_box.children = (VBox(children=( HBox(children=( VBox(children=( Box( layout=layout_flex_end, children=(save_map, self.product_select), ), HBox((self.slider["Z"], self.plot_box)), HBox((self.slider["X"], ), layout=layout_flex_end), )), VBox( layout=Layout(), children=( Box( children=(save_spe, self.spectrum_select), layout=layout_flex_end, ), self.spectrum_box, ), ), )), HBox((self.timeseriesOutput, )), )), ) for widget in (self.step_slider, self.play): widget.value = 0 widget.max = len(self.settings.output_steps) - 1 for j, xz in enumerate(("X", "Z")): slider = self.slider[xz] mx = self.settings.grid[j] slider.max = mx slider.value = (0, mx) self.replot()
def get(self): super().get() const.convert_to(self.buffer, const.si.gram / const.si.kilogram) return self.buffer
def get(self, unit=const.si.micrometre): self.download_moment_to_buffer('volume', rank=1 / 3) self.buffer[:] *= phys.radius(volume=1) const.convert_to(self.buffer, unit) return self.buffer
def get(self): self.download_moment_to_buffer('volume', rank=0) self.buffer[:] /= self.particles.mesh.dv const.convert_to(self.buffer, const.si.centimetre**-3) return self.buffer
def reinit(self, products): self.products = products self.product_select.options = [(f"{val.description} [{val.unit}]", key) for key, val in products.items() if len(val.shape) == 2] r_bins = phys.radius(volume=self.settings.v_bins) const.convert_to(r_bins, const.si.micrometres) self.spectrumOutput = Output() with self.spectrumOutput: self.spectrumPlot = _SpectrumPlot(r_bins) clear_output() self.timeseriesOutput = Output() with self.timeseriesOutput: default_figsize = rcParams["figure.figsize"] fig_kw = { 'figsize': (2.25 * default_figsize[0], default_figsize[1] / 2) } fig, ax = pyplot.subplots(1, 1, **fig_kw) self.timeseriesPlot = _TimeseriesPlot( fig, ax, self.settings.steps * self.settings.dt) clear_output() self.plots = {} self.outputs = {} for key, product in products.items(): if len(product.shape) == 2: self.outputs[key] = Output() with self.outputs[key]: fig, ax = pyplot.subplots(1, 1) self.plots[key] = _ImagePlot(fig, ax, self.settings.grid, self.settings.size, product, show=True, lines=True) clear_output() self.plot_box = Box() if len(products.keys()) > 0: layout_flex_end = Layout(display='flex', justify_content='flex-end') save_map = Button(icon='save') save_map.on_click(self.handle_save_map) save_spe = Button(icon='save') save_spe.on_click(self.handle_save_spe) self.plots_box.children = (VBox(children=(HBox(children=( VBox(children=( Box(layout=layout_flex_end, children=(save_map, )), HBox((self.slider['Z'], self.plot_box)), HBox((self.slider['X'], ), layout=layout_flex_end))), VBox(layout=Layout(), children=(save_spe, self.spectrumOutput)) )), HBox((self.timeseriesOutput, )))), ) for widget in (self.step_slider, self.play): widget.value = 0 widget.max = len(self.settings.steps) - 1 for j, xz in enumerate(('X', 'Z')): slider = self.slider[xz] mx = self.settings.grid[j] slider.max = mx slider.value = (0, mx) self.replot()