def __init__(self, Acc_Forg: float = 0.2, Acc_N2: float = 134): Aitken = { "palmitic": 0.2, "(NH4)2SO4": 0.8, "NaCl": 0, } Accumulation = { "palmitic": Acc_Forg, "(NH4)2SO4": 0, "NaCl": (1 - Acc_Forg), } super().__init__( ionic_dissociation_phi={ "palmitic": 1, "(NH4)2SO4": 3, "NaCl": 2, }, is_soluble={ "palmitic": False, "(NH4)2SO4": True, "NaCl": True, }, densities={ "palmitic": 0.852 * si.g / si.cm**3, "(NH4)2SO4": 1.77 * si.g / si.cm**3, "NaCl": 2.16 * si.g / si.cm**3, }, compounds=("palmitic", "(NH4)2SO4", "NaCl"), molar_masses={ "palmitic": 256.4 * si.g / si.mole, "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass * si.gram / si.mole, "NaCl": Substance.from_formula("NaCl").mass * si.gram / si.mole, }, ) self.modes = ( { "f_org": 1 - self.f_soluble_volume(Aitken), "kappa": self.kappa(Aitken), "nu_org": self.nu_org(Aitken), "spectrum": spectra.Lognormal( norm_factor=226 / si.cm**3, m_mode=19.6 * si.nm, s_geom=1.71 ), }, { "f_org": 1 - self.f_soluble_volume(Accumulation), "kappa": self.kappa(Accumulation), "nu_org": self.nu_org(Accumulation), "spectrum": spectra.Lognormal( norm_factor=Acc_N2 / si.cm**3, m_mode=69.5 * si.nm, s_geom=1.7 ), }, )
def __init__(self, Acc_Forg: float = 0.3, Acc_N2: float = 30): Ultrafine = { "SOA1": 0.52, "SOA2": 0, "(NH4)2SO4": 0.48, } Accumulation = { "SOA1": 0, "SOA2": Acc_Forg, "(NH4)2SO4": (1 - Acc_Forg), } super().__init__( ionic_dissociation_phi={ "SOA1": 1, "SOA2": 1, "(NH4)2SO4": 3, }, molar_masses={ "SOA1": 190 * si.g / si.mole, "SOA2": 368.4 * si.g / si.mole, "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass * si.gram / si.mole, }, densities={ "SOA1": 1.24 * si.g / si.cm**3, "SOA2": 1.2 * si.g / si.cm**3, "(NH4)2SO4": 1.77 * si.g / si.cm**3, }, compounds=("SOA1", "SOA2", "(NH4)2SO4"), is_soluble={ "SOA1": False, "SOA2": False, "(NH4)2SO4": True, }, ) self.modes = ( { "f_org": 1 - self.f_soluble_volume(Ultrafine), "kappa": self.kappa(Ultrafine), "nu_org": self.nu_org(Ultrafine), "spectrum": spectra.Lognormal( norm_factor=2000 / si.cm**3, m_mode=11.5 * si.nm, s_geom=1.71 ), }, { "f_org": 1 - self.f_soluble_volume(Accumulation), "kappa": self.kappa(Accumulation), "nu_org": self.nu_org(Accumulation), "spectrum": spectra.Lognormal( norm_factor=Acc_N2 / si.cm**3, m_mode=100 * si.nm, s_geom=1.70 ), }, )
def __init__( self, M2_sol: float = 0, M2_N: float = 100 / si.cm**3, M2_rad: float = 50 * si.nm, ): super().__init__( compounds=("(NH4)2SO4", "insoluble"), molar_masses={ "(NH4)2SO4": 132.14 * si.g / si.mole, "insoluble": 44 * si.g / si.mole, }, densities={ "(NH4)2SO4": 1.77 * si.g / si.cm**3, "insoluble": 1.77 * si.g / si.cm**3, }, is_soluble={ "(NH4)2SO4": True, "insoluble": False }, ionic_dissociation_phi={ "(NH4)2SO4": 3, "insoluble": 0 }, ) self.modes = ( { "kappa": self.kappa({ "(NH4)2SO4": 1.0, "insoluble": 0.0 }), "spectrum": spectra.Lognormal(norm_factor=100.0 / si.cm**3, m_mode=50.0 * si.nm, s_geom=2.0), }, { "kappa": self.kappa({ "(NH4)2SO4": M2_sol, "insoluble": (1 - M2_sol) }), "spectrum": spectra.Lognormal(norm_factor=M2_N, m_mode=M2_rad, s_geom=2.0), }, )
def __init__(self): nuclei = {"(NH4)2SO4": 1.0} accum = {"(NH4)2SO4": 1.0} coarse = {"(NH4)2SO4": 1.0} super().__init__( ionic_dissociation_phi={"(NH4)2SO4": 3}, molar_masses={ "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass * si.gram / si.mole }, densities={"(NH4)2SO4": 1.77 * si.g / si.cm**3}, compounds=("(NH4)2SO4", ), is_soluble={"(NH4)2SO4": True}, ) self.modes = ( { "kappa": self.kappa(nuclei), "spectrum": spectra.Lognormal(norm_factor=1000.0 / si.cm**3, m_mode=0.008 * si.um, s_geom=1.6), }, { "kappa": self.kappa(accum), "spectrum": spectra.Lognormal(norm_factor=800 / si.cm**3, m_mode=0.034 * si.um, s_geom=2.1), }, { "kappa": self.kappa(coarse), "spectrum": spectra.Lognormal(norm_factor=0.72 / si.cm**3, m_mode=0.46 * si.um, s_geom=2.2), }, )
def __init__( self, n_sd: int = 100, dt_output: float = 1 * si.second, dt_max: float = 1 * si.second, ): self.total_time = 3 * si.hours self.mass_of_dry_air = (1000 * si.kilogram ) # TODO #335 doubled with jupyter si unit self.n_steps = int(self.total_time / (5 * si.second)) # TODO #334 rename to n_output self.n_sd = n_sd self.r_dry, self.n = spectral_sampling.Logarithmic( spectrum=spectra.Lognormal( norm_factor=1000 / si.milligram * self.mass_of_dry_air, m_mode=50 * si.nanometre, s_geom=1.4, ), size_range=(10.633 * si.nanometre, 513.06 * si.nanometre), ).sample(n_sd) self.dt_max = dt_max self.dt_output = dt_output self.r_bins_edges = np.linspace(0 * si.micrometre, 20 * si.micrometre, 101, endpoint=True) self.backend = CPU self.coord = "VolumeLogarithm" self.adaptive = True self.rtol_x = condensation.DEFAULTS.rtol_x self.rtol_thd = condensation.DEFAULTS.rtol_thd self.dt_cond_range = condensation.DEFAULTS.cond_range self.T0 = 284.3 * si.kelvin self.q0 = 7.6 * si.grams / si.kilogram self.p0 = 938.5 * si.hectopascals self.z0 = 600 * si.metres self.kappa = 0.53 # Petters and S. M. Kreidenweis mean growth-factor derived self.t0 = 1200 * si.second self.f0 = 1 / 1000 * si.hertz
def __init__(self, Forg: float = 0.8, N: float = 400): mode = { "(NH4)2SO4": (1 - Forg), "apinene_dark": Forg, } super().__init__( compounds=("(NH4)2SO4", "apinene_dark"), molar_masses={ "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass * si.gram / si.mole, "apinene_dark": 209 * si.gram / si.mole, }, densities={ "(NH4)2SO4": 1.77 * si.g / si.cm**3, "apinene_dark": 1.27 * si.g / si.cm**3, }, is_soluble={ "(NH4)2SO4": False, "apinene_dark": True, }, ionic_dissociation_phi={ "(NH4)2SO4": 3, "apinene_dark": 1, }, ) self.modes = ({ "f_org": 1 - self.f_soluble_volume(mode), "kappa": self.kappa(mode), "nu_org": self.nu_org(mode), "spectrum": spectra.Lognormal(norm_factor=N / si.cm**3, m_mode=50.0 * si.nm, s_geom=1.75), }, )
def __init__(self, Acc_Forg: float = 0.668, Acc_N2: float = 540): # note: SOA1 or SOA2 unclear from the paper Aitken = { "SOA1": 0.668, "SOA2": 0, "(NH4)2SO4": 0.166, "NH4NO3": 0.166, } Accumulation = { "SOA1": 0, "SOA2": Acc_Forg, "(NH4)2SO4": (1 - Acc_Forg) / 2, "NH4NO3": (1 - Acc_Forg) / 2, } super().__init__( ionic_dissociation_phi={ "SOA1": 1, "SOA2": 1, "(NH4)2SO4": 3, "NH4NO3": 2, }, molar_masses={ "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass * si.gram / si.mole, "NH4NO3": Substance.from_formula("NH4NO3").mass * si.gram / si.mole, "SOA1": 190 * si.g / si.mole, "SOA2": 368.4 * si.g / si.mole, }, densities={ "SOA1": 1.24 * si.g / si.cm**3, "SOA2": 1.2 * si.g / si.cm**3, "(NH4)2SO4": 1.77 * si.g / si.cm**3, "NH4NO3": 1.72 * si.g / si.cm**3, }, compounds=("SOA1", "SOA2", "(NH4)2SO4", "NH4NO3"), is_soluble={ "SOA1": False, "SOA2": False, "(NH4)2SO4": True, "NH4NO3": True, }, ) self.modes = ( { "f_org": 1 - self.f_soluble_volume(Aitken), "kappa": self.kappa(Aitken), "nu_org": self.nu_org(Aitken), "spectrum": spectra.Lognormal( norm_factor=1100 / si.cm**3, m_mode=22.7 * si.nm, s_geom=1.75 ), }, { "f_org": 1 - self.f_soluble_volume(Accumulation), "kappa": self.kappa(Accumulation), "nu_org": self.nu_org(Accumulation), "spectrum": spectra.Lognormal( norm_factor=Acc_N2 / si.cm**3, m_mode=82.2 * si.nm, s_geom=1.62, ), }, )
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)
def __init__(self, formulae: Formulae): self.formulae = formulae const = formulae.constants self.condensation_rtol_x = condensation.DEFAULTS.rtol_x self.condensation_rtol_thd = condensation.DEFAULTS.rtol_thd self.condensation_adaptive = True self.condensation_substeps = -1 self.condensation_dt_cond_range = condensation.DEFAULTS.cond_range self.condensation_schedule = condensation.DEFAULTS.schedule self.coalescence_adaptive = True self.coalescence_dt_coal_range = collisions.collision.DEFAULTS.dt_coal_range self.coalescence_optimized_random = True self.coalescence_substeps = 1 self.kernel = Geometric(collection_efficiency=1) self.freezing_singular = True self.freezing_inp_spec = None self.displacement_adaptive = displacement.DEFAULTS.adaptive self.displacement_rtol = displacement.DEFAULTS.rtol self.n_sd_per_gridbox = 20 self.aerosol_radius_threshold = 0.5 * si.micrometre self.drizzle_radius_threshold = 25 * si.micrometre self.r_bins_edges = np.logspace( np.log10(0.001 * si.micrometre), np.log10(100 * si.micrometre), 64, endpoint=True, ) self.T_bins_edges = np.linspace(const.T0 - 40, const.T0 - 20, 64, endpoint=True) # TODO #599 n_bins_per_phase = 25 solid_phase_radii = ( np.linspace(-n_bins_per_phase, -1, n_bins_per_phase + 1) * si.um) liquid_phase_radii = ( np.linspace(0, n_bins_per_phase, n_bins_per_phase + 1) * si.um) self.terminal_velocity_radius_bin_edges = np.concatenate( [solid_phase_radii, liquid_phase_radii]) self.output_interval = 1 * si.minute self.spin_up_time = 0 self.mode_1 = spectra.Lognormal( norm_factor=60 / si.centimetre**3 / const.rho_STP, m_mode=0.04 * si.micrometre, s_geom=1.4, ) self.mode_2 = spectra.Lognormal( norm_factor=40 / si.centimetre**3 / const.rho_STP, m_mode=0.15 * si.micrometre, s_geom=1.6, ) self.spectrum_per_mass_of_dry_air = spectra.Sum( (self.mode_1, self.mode_2)) self.kappa = 1 # TODO #441! self.processes = { "particle advection": True, "fluid advection": True, "coalescence": True, "condensation": True, "sedimentation": True, "freezing": False, } self.mpdata_iters = 2 self.mpdata_iga = True self.mpdata_fct = True self.mpdata_tot = True key_packages = [PySDM, PyMPDATA, numba, numpy, scipy] try: import ThrustRTC # pylint: disable=import-outside-toplevel key_packages.append(ThrustRTC) except: # pylint: disable=bare-except pass self.versions = {} for pkg in key_packages: try: self.versions[pkg.__name__] = pkg.__version__ except AttributeError: pass self.versions = str(self.versions) self.dt = None self.simulation_time = None self.grid = None self.p0 = None self.qv0 = None self.th_std0 = None self.size = None
def __init__( self, *, n_sd_per_gridbox: int, p0: float = 1007 * si.hPa, # as used in Olesik et al. 2022 (GMD) particle_reservoir_depth: float = 0 * si.m, kappa: float = 1, rho_times_w_1: float = 2 * si.m / si.s * si.kg / si.m**3, dt: float = 1 * si.s, dz: float = 25 * si.m, precip: bool = True, breakup: bool = False ): self.formulae = Formulae() self.n_sd_per_gridbox = n_sd_per_gridbox self.kappa = kappa self.wet_radius_spectrum_per_mass_of_dry_air = spectra.Lognormal( norm_factor=50 / si.cm**3 / self.formulae.constants.rho_STP, m_mode=0.08 / 2 * si.um, s_geom=1.4, ) self.particle_reservoir_depth = particle_reservoir_depth self.dt = dt self.dz = dz self.precip = precip self.breakup = breakup self.z_max = 3000 * si.metres self.t_max = 60 * si.minutes t_1 = 600 * si.s self.rho_times_w = ( lambda t: rho_times_w_1 * np.sin(np.pi * t / t_1) if t < t_1 else 0 ) self._th = interp1d( (0.0 * si.m, 740.0 * si.m, 3260.00 * si.m), (297.9 * si.K, 297.9 * si.K, 312.66 * si.K), fill_value="extrapolate", ) self.qv = interp1d( (-max(particle_reservoir_depth, 1), 0, 740, 3260), (0.015, 0.015, 0.0138, 0.0024), fill_value="extrapolate", ) self.thd = ( lambda z_above_reservoir: self.formulae.state_variable_triplet.th_dry( self._th(z_above_reservoir), self.qv(z_above_reservoir) ) ) g = self.formulae.constants.g_std self.rhod0 = self.formulae.state_variable_triplet.rho_d( p=p0, qv=self.qv(0 * si.m), theta_std=self._th(0 * si.m), ) def drhod_dz(z_above_reservoir, rhod): if z_above_reservoir < 0: return 0 qv = self.qv(z_above_reservoir) T = self.formulae.state_variable_triplet.T( rhod[0], self.thd(z_above_reservoir) ) p = self.formulae.state_variable_triplet.p(rhod[0], T, qv) lv = self.formulae.latent_heat.lv(T) return self.formulae.hydrostatics.drho_dz( g, p, T, qv, lv ) # note: drho \approx drhod z_span = (-self.particle_reservoir_depth, self.z_max) z_points = np.linspace(*z_span, 2 * self.nz + 1) rhod_solution = solve_ivp( fun=drhod_dz, t_span=z_span, y0=np.asarray((self.rhod0,)), t_eval=z_points, max_step=dz / 2, ) assert rhod_solution.success self.rhod = interp1d(z_points, rhod_solution.y[0]) self.mpdata_settings = {"n_iters": 3, "iga": True, "fct": True, "tot": True} self.condensation_rtol_x = condensation.DEFAULTS.rtol_x self.condensation_rtol_thd = condensation.DEFAULTS.rtol_thd self.condensation_adaptive = True self.coalescence_adaptive = True self.r_bins_edges = np.logspace( np.log10(0.001 * si.um), np.log10(100 * si.um), 101, endpoint=True ) self.cloud_water_radius_range = [1 * si.um, 50 * si.um] self.rain_water_radius_range = [50 * si.um, np.inf * si.um]