def create_state_2d(self, extensive, intensive, spatial_discretisation, spectral_discretisation, spectrum_per_mass_of_dry_air, r_range, kappa, radius_threshold): assert_not_none(self.particles.mesh, self.particles.environment) assert_none(self.particles.state) with np.errstate(all='raise'): positions = spatial_discretisation(self.particles.mesh.grid, self.particles.n_sd) cell_id, cell_origin, position_in_cell = self.particles.mesh.cellular_attributes( positions) r_dry, n_per_kg = spectral_discretisation( self.particles.n_sd, spectrum_per_mass_of_dry_air, r_range) r_wet = r_wet_init(r_dry, self.particles.environment, cell_id, kappa) n_per_m3 = n_init(n_per_kg, self.particles.environment, self.particles.mesh, cell_id) n = discretise_n(n_per_m3) extensive['volume'] = phys.volume(radius=r_wet) extensive['dry volume'] = phys.volume(radius=r_dry) self.particles.state = StateFactory.state(n, intensive, extensive, cell_id, cell_origin, position_in_cell, self.particles) for product in [ TotalParticleConcentration(self.particles), TotalParticleSpecificConcentration(self.particles), AerosolConcentration(self.particles, radius_threshold), AerosolSpecificConcentration(self.particles, radius_threshold), ParticleMeanRadius(self.particles), SuperDropletCount(self.particles) ]: self.register_product(product)
def __init__(self, setup): t_half = setup.z_half / setup.w_avg dt = (2 * t_half) / setup.n_steps self.particles = Particles(backend=setup.backend, n_sd=1, dt=dt) self.particles.set_mesh_0d() self.particles.set_environment( MoistLagrangianParcelAdiabatic, { "mass_of_dry_air": setup.mass_of_dry_air, "p0": setup.p0, "q0": setup.q0, "T0": setup.T0, "w": setup.w }) r_dry = np.array([setup.r_dry]) x_dry = phys.volume(radius=r_dry) n = np.array([setup.n_in_dv], dtype=np.int64) r_wet = r_wet_init(r_dry, self.particles.environment, np.zeros_like(n), setup.kappa) v_wet = phys.volume(radius=r_wet) self.particles.create_state_0d(n=n, extensive={ 'dry volume': x_dry, 'volume': v_wet }, intensive={}) self.particles.add_dynamic(Condensation, {"kappa": setup.kappa}) self.n_steps = setup.n_steps
def __init__(self, setup): t_half = setup.z_half / setup.w_avg dt_output = (2 * t_half) / setup.n_steps self.n_substeps = 1 while dt_output / self.n_substeps >= setup.dt_max: # TODO dt_max self.n_substeps += 1 particles_builder = ParticlesBuilder(backend=setup.backend, n_sd=1, dt=dt_output / self.n_substeps) particles_builder.set_mesh_0d() particles_builder.set_environment(MoistLagrangianParcelAdiabatic, { "mass_of_dry_air": setup.mass_of_dry_air, "p0": setup.p0, "q0": setup.q0, "T0": setup.T0, "w": setup.w }) r_dry = np.array([setup.r_dry]) x_dry = phys.volume(radius=r_dry) n = np.array([setup.n_in_dv], dtype=np.int64) r_wet = r_wet_init(r_dry, particles_builder.particles.environment, np.zeros_like(n), setup.kappa) v_wet = phys.volume(radius=r_wet) particles_builder.create_state_0d(n=n, extensive={'dry volume': x_dry, 'volume': v_wet}, intensive={}) particles_builder.register_dynamic(Condensation, { "kappa": setup.kappa, "scheme": setup.scheme, "rtol_lnv": setup.rtol_lnv, "rtol_thd": setup.rtol_thd, }) self.particles = particles_builder.get_particles() self.n_steps = setup.n_steps
class SetupA: x_min = phys.volume(radius=10 * si.micrometres) # not given in the paper x_max = phys.volume(radius=100 * si.micrometres) # not given in the paper n_sd = 2 ** 13 n_part = 2 ** 23 / si.metre**3 X0 = 4 / 3 * np.pi * 30.531e-6 ** 3 dv = 1e6 * si.metres**3 norm_factor = n_part * dv rho = 1000 * si.kilogram / si.metre**3 dt = 1 * si.seconds seed = 44 steps = [0, 1200, 2400, 3600] kernel = Golovin(b=1.5e3 / si.second) spectrum = Exponential(norm_factor=norm_factor, scale=X0) backend = Default # TODO: rename? # TODO: as backend method? def check(self, state, step): check_LWC = 1e-3 * si.kilogram / si.metre**3 check_ksi = self.n_part * self.dv / self.n_sd # multiplicities if step == 0: np.testing.assert_approx_equal(np.amin(state['n']), np.amax(state['n']), 1) np.testing.assert_approx_equal(state['n'][0], check_ksi, 1) # liquid water content LWC = self.rho * np.dot(state['n'], state['volume']) / self.dv np.testing.assert_approx_equal(LWC, check_LWC, 3)
def __init__(self, setup): self.particles = Particles(backend=setup.backend, n_sd=setup.n_sd, dt=setup.dt) self.particles.set_mesh_0d() self.particles.set_environment( MoistLagrangianParcelAdiabatic, { "mass_of_dry_air": setup.mass_of_dry_air, "p0": setup.p0, "q0": setup.q0, "T0": setup.T0, "w": setup.w, "z0": setup.z0 }) v_dry = phys.volume(radius=setup.r_dry) r_wet = r_wet_init(setup.r_dry, self.particles.environment, np.zeros_like(setup.n), setup.kappa) v_wet = phys.volume(radius=r_wet) self.particles.create_state_0d(n=setup.n, extensive={ 'dry volume': v_dry, 'volume': v_wet }, intensive={}) self.particles.add_dynamic(Condensation, {"kappa": setup.kappa}) self.n_steps = setup.n_steps
def create_state_2d(self, extensive, intensive, spatial_discretisation, spectral_discretisation, spectrum_per_mass_of_dry_air, r_range, kappa): assert_not_none(self.mesh, self.environment) assert_none(self.state) with np.errstate(all='raise'): positions = spatial_discretisation(self.mesh.grid, self.n_sd) cell_id, cell_origin, position_in_cell = self.mesh.cellular_attributes(positions) r_dry, n_per_kg = spectral_discretisation(self.n_sd, spectrum_per_mass_of_dry_air, r_range) r_wet = r_wet_init(r_dry, self.environment, cell_id, kappa) n_per_m3 = n_init(n_per_kg, self, cell_id) n = discretise_n(n_per_m3) extensive['volume'] = phys.volume(radius=r_wet) extensive['dry volume'] = phys.volume(radius=r_dry) self.state = StateFactory.state(n, intensive, extensive, cell_id, cell_origin, position_in_cell, self)
def __init__(self, setup): dt_output = setup.total_time / setup.n_steps self.n_substeps = 1 while (dt_output / self.n_substeps >= setup.dt_max): self.n_substeps += 1 particles_builder = ParticlesBuilder(backend=setup.backend, n_sd=setup.n_sd, dt=dt_output / self.n_substeps) particles_builder.set_mesh_0d() particles_builder.set_environment( MoistLagrangianParcelAdiabatic, { "mass_of_dry_air": setup.mass_of_dry_air, "p0": setup.p0, "q0": setup.q0, "T0": setup.T0, "w": setup.w, "z0": setup.z0 }) v_dry = phys.volume(radius=setup.r_dry) r_wet = r_wet_init(setup.r_dry, particles_builder.particles.environment, np.zeros_like(setup.n), setup.kappa) v_wet = phys.volume(radius=r_wet) particles_builder.create_state_0d(n=setup.n, extensive={ 'dry volume': v_dry, 'volume': v_wet }, intensive={}) particles_builder.register_dynamic( Condensation, { "kappa": setup.kappa, "scheme": setup.condensation_scheme, "rtol_lnv": setup.rtol_lnv, "rtol_thd": setup.rtol_thd, }) self.particles = particles_builder.get_particles() self.n_steps = setup.n_steps
def get(self): self.download_moment_to_buffer( 'volume', rank=0, attr_range=[0, phys.volume(self.radius_threshold)]) result = self.buffer.copy() # TODO !!! self.download_to_buffer(self.particles.environment['rhod']) result[:] /= self.particles.mesh.dv result[:] /= self.buffer const.convert_to(result, const.si.milligram**-1) return result
def plot(self, state, t): s = self.setup if t == 0: analytic_solution = s.spectrum.size_distribution else: analytic_solution = lambda x: s.norm_factor * s.kernel.analytic_solution( x=x, t=t, x_0=s.X0, N_0=s.n_part) dm = np.diff(self.v_bins) dr = np.diff(self.r_bins) pdf_m_x = self.v_bins[:-1] + dm / 2 pdf_m_y = analytic_solution(pdf_m_x) pdf_r_x = self.r_bins[:-1] + dr / 2 pdf_r_y = pdf_m_y * dm / dr * pdf_r_x pyplot.plot(pdf_r_x * si.metres / si.micrometres, pdf_r_y * phys.volume(radius=pdf_r_x) * s.rho / s.dv * si.kilograms / si.grams, color='black') vals = np.empty(len(self.r_bins) - 1) tmp = np.empty((1, 1)) moment_0 = state.particles.backend.array(1, dtype=int) moments = state.particles.backend.array((1, 1), dtype=float) for i in range(len(vals)): state.moments(moment_0, moments, specs={'volume': (1, )}, attr_range=(self.v_bins[i], self.v_bins[i + 1])) state.particles.backend.download(moments, tmp) vals[i] = tmp[0, 0] state.particles.backend.download(moment_0, tmp) vals[i] *= tmp[0, 0] vals[i] *= s.rho / s.dv vals[i] /= (np.log(self.r_bins[i + 1]) - np.log(self.r_bins[i])) pyplot.step(self.r_bins[:-1] * si.metres / si.micrometres, vals * si.kilograms / si.grams, where='post', label=f"t = {t}s") pyplot.grid() pyplot.xscale('log') pyplot.xlabel('particle radius [µm]') pyplot.ylabel('dm/dlnr [g/m^3/(unit dr/r)]') pyplot.legend()
def test_initialisation(plot=False): # TODO: seed as a part of setup? setup = Setup() setup.steps = [] setup.grid = (10, 5) setup.n_sd_per_gridbox = 2000 simulation = Simulation(setup, None) n_bins = 32 n_levels = setup.grid[1] n_cell = np.prod(np.array(setup.grid)) n_moments = 1 v_bins = np.logspace((np.log10(phys.volume(radius=setup.r_min))), (np.log10(phys.volume(radius=10 * setup.r_max))), num=n_bins, endpoint=True) r_bins = phys.radius(volume=v_bins) histogram_dry = np.empty((len(r_bins) - 1, n_levels)) histogram_wet = np.empty_like(histogram_dry) moment_0 = setup.backend.array(n_cell, dtype=int) moments = setup.backend.array((n_moments, n_cell), dtype=float) tmp = np.empty(n_cell) # Act (moments) simulation.run() particles = simulation.particles environment = simulation.particles.environment rhod = setup.backend.to_ndarray(environment["rhod"]).reshape( setup.grid).mean(axis=0) for i in range(len(histogram_dry)): particles.state.moments(moment_0, moments, specs={}, attr_name='dry volume', attr_range=(v_bins[i], v_bins[i + 1])) particles.backend.download(moment_0, tmp) histogram_dry[i, :] = tmp.reshape( setup.grid).sum(axis=0) / (particles.mesh.dv * setup.grid[0]) particles.state.moments(moment_0, moments, specs={}, attr_name='volume', attr_range=(v_bins[i], v_bins[i + 1])) particles.backend.download(moment_0, tmp) histogram_wet[i, :] = tmp.reshape( setup.grid).sum(axis=0) / (particles.mesh.dv * setup.grid[0]) # Plot if plot: for level in range(0, n_levels): color = str(.5 * (2 + (level / (n_levels - 1)))) pyplot.step(r_bins[:-1] * si.metres / si.micrometres, histogram_dry[:, level] / si.metre**3 * si.centimetre**3, where='post', color=color, label="level " + str(level)) pyplot.step(r_bins[:-1] * si.metres / si.micrometres, histogram_wet[:, level] / si.metre**3 * si.centimetre**3, where='post', color=color, linestyle='--') pyplot.grid() pyplot.xscale('log') pyplot.xlabel('particle radius [µm]') pyplot.ylabel('concentration per bin [cm^{-3}]') pyplot.legend() pyplot.show() # Assert - location of maximum for level in range(n_levels): real_max = setup.spectrum_per_mass_of_dry_air.distribution_params[2] idx_max_dry = np.argmax(histogram_dry[:, level]) idx_max_wet = np.argmax(histogram_wet[:, level]) assert r_bins[idx_max_dry] < real_max < r_bins[idx_max_dry + 1] assert idx_max_dry < idx_max_wet # Assert - total number for level in reversed(range(n_levels)): mass_conc_dry = np.sum(histogram_dry[:, level]) / rhod[level] mass_conc_wet = np.sum(histogram_wet[:, level]) / rhod[level] mass_conc_STP = setup.spectrum_per_mass_of_dry_air.norm_factor assert .5 * mass_conc_STP < mass_conc_dry < 1.5 * mass_conc_STP np.testing.assert_approx_equal(mass_conc_dry, mass_conc_wet) # Assert - decreasing number density total_above = 0 for level in reversed(range(n_levels)): total_below = np.sum(histogram_dry[:, level]) assert total_below > total_above total_above = total_below
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 __call__(self): self.environment.sync() state = self.particles.state state.sort_by_cell_id( ) # TODO +what about droplets that precipitated out of the domain compute_cell_start(self.cell_start, state.cell_id, state.idx, state.SD_num) v = state.get_backend_storage("volume") n = state.n vdry = state.get_backend_storage("dry volume") if self.scheme == 'scipy.odeint': for cell_id in range(self.particles.mesh.n_cell): cell_start = self.cell_start[cell_id] cell_end = self.cell_start[cell_id + 1] n_sd_in_cell = cell_end - cell_start if n_sd_in_cell == 0: continue y0 = np.empty(n_sd_in_cell + idx_rw) y0[idx_rhod] = self.environment['rhod'][cell_id] y0[idx_thd] = self.environment['thd'][cell_id] y0[idx_qv] = self.environment['qv'][cell_id] y0[idx_rw:] = phys.radius( volume=v[state.idx[cell_start:cell_end]]) integ = ode.solve_ivp( _ODESystem( self.kappa, vdry[state.idx[cell_start:cell_end]], n[state.idx[cell_start:cell_end]], (self.environment.get_predicted("rhod")[cell_id] - self.environment['rhod'][cell_id]) / self.dt, (self.environment.get_predicted('thd')[cell_id] - self.environment['thd'][cell_id]) / self.dt, (self.environment.get_predicted('qv')[cell_id] - self.environment['qv'][cell_id]) / self.dt, self.environment.get_predicted("rhod")[cell_id] * self.environment.dv), (0., self.dt), y0, method='BDF', # rtol=1e-6, atol=1e-9, # first_step=self.dt, t_eval=[self.dt]) assert integ.success, integ.message dm = 0 for i in range(cell_end - cell_start): x_new = phys.volume(radius=integ.y[idx_rw + i]) x_old = v[state.idx[cell_start + i]] nd = n[state.idx[cell_start + i]] dm += nd * (x_new - x_old) * rho_w v[state.idx[cell_start + i]] = x_new m_d = self.environment.get_predicted( 'rhod')[cell_id] * self.environment.dv dq_sum = -dm / m_d dq_ode = integ.y[idx_qv] - self.environment.get_predicted( 'qv')[cell_id] #dth_sum = dth_ode = integ.y[idx_thd] - self.environment.get_predicted( 'thd')[cell_id] # TODO: move to a separate test #np.testing.assert_approx_equal(dq_ode, dq_sum, 4) #np.testing.assert_approx_equal(dth_ode, dth_sum) self.environment.get_predicted('qv')[cell_id] += dq_sum self.environment.get_predicted('thd')[cell_id] += dth_ode else: raise NotImplementedError()