コード例 #1
0
ファイル: aqueous_chemistry.py プロジェクト: trontrytel/PySDM
    def register(self, builder):
        self.core = builder.core

        for key, compound in GASEOUS_COMPOUNDS.items():
            shape = (1, )  # TODO #440
            self.environment_mixing_ratios[compound] = np.full(
                shape,
                self.core.formulae.trivia.mole_fraction_2_mixing_ratio(
                    self.environment_mole_fractions[compound],
                    SPECIFIC_GRAVITY[compound]))
        self.environment_mole_fractions = None

        if self.pH_H_max is None:
            self.pH_H_max = self.core.formulae.trivia.pH2H(default_pH_min)
        if self.pH_H_min is None:
            self.pH_H_min = self.core.formulae.trivia.pH2H(default_pH_max)

        for key in AQUEOUS_COMPOUNDS.keys():
            builder.request_attribute("conc_" + key)

        for key in self.core.backend.KINETIC_CONST.KINETIC_CONST.keys():
            self.kinetic_consts[key] = self.core.Storage.empty(
                self.core.mesh.n_cell, dtype=float)
        for key in self.core.backend.EQUILIBRIUM_CONST.EQUILIBRIUM_CONST.keys(
        ):
            self.equilibrium_consts[key] = self.core.Storage.empty(
                self.core.mesh.n_cell, dtype=float)
        for key in DIFFUSION_CONST.keys():
            self.dissociation_factors[key] = self.core.Storage.empty(
                self.core.n_sd, dtype=float)
        self.do_chemistry_flag = self.core.Storage.empty(self.core.n_sd,
                                                         dtype=bool)
コード例 #2
0
ファイル: pH.py プロジェクト: edejong-caltech/PySDM
 def __init__(self, builder):
     self.conc = {}
     for k, v in AQUEOUS_COMPOUNDS.items():
         if len(v) > 1:
             self.conc[k] = builder.get_attribute('conc_' + k)
     super().__init__(builder, name='pH', dependencies=self.conc.values())
     self.environment = builder.particulator.environment
     self.cell_id = builder.get_attribute('cell id')
     self.particles = builder.particulator
コード例 #3
0
ファイル: test_Table_3.py プロジェクト: edejong-caltech/PySDM
    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)

        # Act
        output = simulation.run()

        # Assert
        np.testing.assert_allclose(output['RH_env'][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_env']),
                                   desired=mass_conc_NH4p + mass_conc_SO4mm +
                                   mass_conc_H,
                                   rtol=rtol)

        expected = {k: 0 for k in AQUEOUS_COMPOUNDS.keys()}
        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.keys():
            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_GRAVITY[compound])
                        * np.asarray(output['rhod_env'])),
                desired=expected[key],
                rtol=rtol)
コード例 #4
0
from PySDM.attributes.chemistry.concentration import Concentration
from PySDM.attributes.chemistry.pH import pH
from PySDM.attributes.chemistry.hydrogen_ion_concentration import HydrogenIonConcentration
from PySDM.physics.aqueous_chemistry.support import AQUEOUS_COMPOUNDS
from functools import partial

attributes = {
    'n': lambda _: Multiplicities,
    'volume': lambda _: Volume,
    'dry volume': lambda dynamics: DryVolumeDynamic if 'AqueousChemistry' in dynamics else DryVolumeStatic,
    'radius': lambda _: Radius,
    'dry radius': lambda _: DryRadius,
    'terminal velocity': lambda _: TerminalVelocity,
    'cell id': lambda _: CellID,
    'cell origin': lambda _: CellOrigin,
    'position in cell': lambda _: PositionInCell,
    'temperature': lambda _: Temperature,
    'heat': lambda _: Heat,
    'critical volume': lambda _: CriticalVolume,
    **{"moles_" + compound: partial(lambda _, c: MoleAmount(c), c=compound)
       for compound in AQUEOUS_COMPOUNDS.keys()},
    **{"conc_" + compound: partial(lambda _, c: Concentration(c), c=compound)
       for compound in AQUEOUS_COMPOUNDS.keys()},
    'pH': lambda _: pH,
    'conc_H': lambda _: HydrogenIonConcentration
}


def get_class(name, dynamics):
    return attributes[name](dynamics)
コード例 #5
0
    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')

        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 = .5 * si.m / si.s
        self.g = 10 * si.m / si.s**2

        self.n_sd = n_sd
        self.n_substep = n_substep

        self.p0 = 950 * si.mbar
        self.T0 = 285.2 * si.K
        pv0 = .95 * self.formulae.saturation_vapour_pressure.pvs_Celsius(
            self.T0 - const.T0)
        self.q0 = const.eps * pv0 / (self.p0 - pv0)
        self.kappa = .61

        self.cloud_radius_range = (.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=.08 * si.um / 2,
                                       s_geom=2)).sample(n_sd)

        self.ENVIRONMENT_MOLE_FRACTIONS = {
            "SO2": 0.2 * const.ppb,
            "O3": 50 * const.ppb,
            "H2O2": 0.5 * const.ppb,
            "CO2": 360 * const.ppm,
            "HNO3": 0.1 * const.ppb,
            "NH3": 0.1 * const.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.keys()
        }

        self.dry_radius_bins_edges = np.logspace(
            np.log10(.01 * si.um), np.log10(1 * si.um), 51, endpoint=True) / 2
コード例 #6
0
    def __init__(self, settings, products=None):
        env = Parcel(dt=settings.dt,
                     mass_of_dry_air=settings.mass_of_dry_air,
                     p0=settings.p0,
                     q0=settings.q0,
                     T0=settings.T0,
                     w=settings.w,
                     g=settings.g)

        builder = Builder(n_sd=settings.n_sd,
                          backend=CPU,
                          formulae=settings.formulae)
        builder.set_environment(env)

        attributes = env.init_attributes(n_in_dv=settings.n_in_dv,
                                         kappa=settings.kappa,
                                         r_dry=settings.r_dry)
        attributes = {
            **attributes,
            **settings.starting_amounts, 'pH': np.zeros(settings.n_sd)
        }

        builder.add_dynamic(AmbientThermodynamics())
        builder.add_dynamic(Condensation(kappa=settings.kappa))
        builder.add_dynamic(
            AqueousChemistry(settings.ENVIRONMENT_MOLE_FRACTIONS,
                             system_type=settings.system_type,
                             n_substep=settings.n_substep,
                             dry_rho=settings.DRY_RHO,
                             dry_molar_mass=settings.dry_molar_mass))

        products = products or (
            PySDM_products.RelativeHumidity(),
            PySDM_products.WaterMixingRatio(name='ql',
                                            description_prefix='liquid',
                                            radius_range=[1 * si.um, np.inf]),
            PySDM_products.ParcelDisplacement(), PySDM_products.Pressure(),
            PySDM_products.Temperature(), PySDM_products.DryAirDensity(),
            PySDM_products.WaterVapourMixingRatio(), PySDM_products.Time(), *[
                PySDM_products.AqueousMoleFraction(compound)
                for compound in AQUEOUS_COMPOUNDS.keys()
            ], *[
                PySDM_products.GaseousMoleFraction(compound)
                for compound in GASEOUS_COMPOUNDS.keys()
            ],
            PySDM_products.pH(radius_range=settings.cloud_radius_range,
                              weighting='number',
                              attr='pH'),
            PySDM_products.pH(radius_range=settings.cloud_radius_range,
                              weighting='volume',
                              attr='pH'),
            PySDM_products.pH(radius_range=settings.cloud_radius_range,
                              weighting='number',
                              attr='conc_H'),
            PySDM_products.pH(radius_range=settings.cloud_radius_range,
                              weighting='volume',
                              attr='conc_H'),
            PySDM_products.TotalDryMassMixingRatio(
                settings.DRY_RHO), PySDM_products.PeakSupersaturation(),
            PySDM_products.CloudDropletConcentration(
                radius_range=settings.cloud_radius_range),
            PySDM_products.AqueousMassSpectrum("S_VI",
                                               settings.dry_radius_bins_edges))

        self.core = builder.build(attributes=attributes, products=products)
        self.settings = settings