{'fraction_non_alphanumeric': 0.03231133768938034, 'fraction_numerical': 0.008257341853952754, 'mean_word_length': 5.412523020257827, 'pattern_counts': {'":': 0, '<': 0, '<?xml version=': 0, '>': 0, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
PySDM v1: particle-based cloud modelling package for warm-rain microphysics and aqueous chemistry

PySDM is an open-source Python package for simulating the dynamics of particles undergoing condensational and collisional growth, interacting with a fluid flow and subject to chemical composition changes. It is intended to serve as a building block for process-level as well as computational-fluid-dynamics simulation systems involving representation of a continuous phase (air) and a dispersed phase (aerosol), with PySDM being responsible for representation of the dispersed phase. The PySDM package core is a Pythonic high-performance implementation of the Super-Droplet Method (SDM) Monte-Carlo algorithm for representing collisional growth, hence the name. PySDM has two alternative parallel number-crunching backends available: multi-threaded CPU backend based on Numba and GPU-resident backend built on top of ThrustRTC. The usage examples are built on top of four simple atmospheric cloud modelling frameworks: box, adiabatic parcel, single-column and 2D prescribed flow kinematic models. In addition, the package ships with tutorial code depicting how PySDM can be used from Julia and Matlab.


Introduction
PySDM is an open-source Python package for simulating the dynamics of particles undergoing condensational and collisional growth, interacting with a fluid flow and subject to chemical composition changes. It is intended to serve as a building block for process-level as well as computational-fluid-dynamics simulation systems involving representation of a continuous phase (air) and a dispersed phase (aerosol), with PySDM being responsible for representation of the dispersed phase. As of the major version 1 (v1), the development has been focused on atmospheric cloud physics applications, in particular on modelling the dynamics of particles immersed in moist air using the particle-based approach to represent the evolution of the size spectrum of aerosol/cloud/rain particles. The particle-based approach contrasts the more commonly used bulk and bin methods in which atmospheric particles are segregated into multiple categories (aerosol, cloud, rain) and their evolution is governed by deterministic dynamics solved on the same Eulerian grid as the dynamics of the continuous phase. Particle-based methods employ discrete computational (super) particles for modelling the dispersed phase. Each super particle is associated with a set of continuously-valued attributes evolving in Lagrangian manner. Such approach is particularly well suited for using probabilistic representation of particle collisional growth (coagulation) and for representing processes dependent on numerous particle attributes which helps to overcome the limitations of bulk and bin methods (Morrison et al., 2020).
The PySDM package core is a Pythonic high-performance implementation of the Super-Droplet Method (SDM) Monte-Carlo algorithm for representing collisional growth (Shima et al., 2009), hence the name. The SDM is a probabilistic alternative to the mean-field approach embodied by the Smoluchowski equation, for a comparative outline of both approaches see . In atmospheric aerosol-cloud interactions, particle collisional growth is responsible for formation of rain drops through collisions of smaller cloud droplets (warm-rain process) as well as for aerosol washout.
Besides collisional growth, PySDM includes representation of condensation/evaporation of water vapour on/from the particles. Furthermore, representation of dissolution and, if applicable, dissociation of trace gases (sulfur dioxide, ozone, hydrogen peroxide, carbon dioxide, nitric acid and ammonia) is included to model the subsequent aqueous-phase oxidation of the dissolved sulfur dioxide. Representation of the chemical processes follows the particle-based formulation of Jaruga & Pawlowska (2018).
The usage examples are built on top of four different environment classes included in PySDM v1 and implementing common simple atmospheric cloud modelling frameworks: box, adiabatic parcel, single-column and 2D prescribed flow kinematic models.
In addition, the package ships with tutorial code depicting how PySDM can be used from Julia and Matlab using the PyCall.jl and the Matlab-bundled Python interface, respectively. Two exporter classes are available as of time of writing enabling storage of particle attributes in the VTK format and storage of gridded products in netCDF format.

Dependencies and supported platforms
PySDM essential dependencies are: NumPy, SciPy, Numba, Pint and ChemPy which are all free and open-source software available via the PyPI platform. PySDM ships with a setup.py file allowing installation using the pip package manager (i.e., pip install git+https://g ithub.com/atmos-cloud-sim-uj/PySDM.git).
PySDM has two alternative parallel number-crunching backends available: multi-threaded CPU backend based on Numba (Lam et al., 2015) and GPU-resident backend built on top of ThrustRTC (Yang, 2020). The optional GPU backend relies on proprietary vendor-specific CUDA technology, the accompanying non-free software and drivers; ThrustRTC and CURandRTC packages are released under the Anti-996 license.
The usage examples for Python were developed embracing the Jupyter interactive platform allowing control of the simulations via web browser. All Python examples are ready for use with the mybinder.org and the Google Colab platforms.
Continuous integration infrastructure used in the development of PySDM assures the targeted full usability on Linux, macOS and Windows environments. Compatibility with Python versions 3.7 through 3.9 is maintained as of time of writing. Test coverage for PySDM is reported using the codecov.io platform. Coverage analysis of the backend code requires execution with JIT-compilation disabled for the CPU backend (e.g., using the NUMBA DISABLE JIT=1 environment variable setting). For the GPU backend, a purpose-built FakeThrust class is shipped with PySDM which implements a subset of the ThrustRTC API and translates C++ kernels into equivalent Numba parallel Python code for debugging and coverage analysis.
The Pint dimensional analysis package is used for unit testing. It allows asserting on the dimensionality of arithmetic expressions representing physical formulae. In order to enable JIT compilation of the formulae for simulation runs, a purpose-built FakeUnitRegistry class that mocks the Pint API reducing its functionality to SI prefix handling is used by default outside of tests.

API in brief
In order to depict PySDM API with a practical example, the following listings provide sample code roughly reproducing the Figure  It is a coalescence-only set-up in which the initial particle size spectrum is exponential and is deterministically sampled to match the condition of each super-droplet having equal initial multiplicity, with the multiplicity denoting the number of real particles represented by a single computational particle referred to as a super-droplet: from PySDM.physics import si from PySDM.initialisation.spectral sampling import ConstantMultiplicity from PySDM.physics.spectra import Exponential n sd = 2 ** 17 . PySDM v1: particle-based cloud modelling package for warm-rain microphysics and aqueous chemistry. initial spectrum = Exponential( norm factor=8.39e12, scale=1.19e5 * si.um ** 3) attributes = {} spectral sampling = ConstantMultiplicity(spectrum=initial spectrum) attributes['volume'], attributes['n'] = spectral sampling.sample(n sd=n sd) In the above snippet, the si is an instance of the FakeUnitRegistry class. The exponential distribution of particle volumes is sampled at 2 17 points in order to initialise two key attributes of the super-droplets, namely their volume and multiplicity. Subsequently, a Builder object is created to orchestrate dependency injection while instantiating the Particulator class of PySDM: import numpy as np from PySDM.builder import Builder from PySDM.environments import Box from PySDM.dynamics import Coalescence from PySDM.physics.coalescence kernels import Golovin from PySDM.backends import CPU from PySDM.products import ParticlesVolumeSpectrum radius bins edges = np.logspace( np.log10(10 * si.um), np.log10(5e3 * si.um), num=32) builder = Builder(n sd=n sd, backend=CPU()) builder.set environment(Box(dt=1 * si.s, dv=1e6 * si.m ** 3)) builder.add dynamic(Coalescence(kernel=Golovin(b=1.5e3 / si.s))) products = [ParticlesVolumeSpectrum(radius bins edges)] particulator = builder.build(attributes, products) The backend argument may be set to an instance of either CPU or GPU what translates to choosing the multi-threaded Numba-based backend or the ThrustRTC-based GPU-resident computation mode, respectively. The employed Box environment corresponds to a zerodimensional framework (particle positions are neglected). The SDM Monte-Carlo coalescence algorithm is added as the only dynamic in the system (other dynamics available as of v1.3 represent condensational growth, particle displacement, aqueous chemistry, ambient thermodynamics and Eulerian advection). Finally, the build() method is used to obtain an instance of the Particulator class which can then be used to control time-stepping and access simulation state through the products registered with the builder. A minimal simulation example is depicted below with a code snippet and a resultant plot ( Figure 1):

Usage examples
The PySDM examples are shipped in a separate package that can be installed with pip All examples feature a settings.py file with simulation parameters, a simulation.py file including logic analogous to the one presented in the code snippets above for handling composition of PySDM components using the Builder class, and a Jupyter notebook file with simulation launching code and basic result visualisation.

Box environment examples
The The second example based on the Box environment, also featuring collision-only setup reproduces several figures from the work of Berry (1966) involving more sophisticated collision kernels representing such phenomena as the geometric sweep-out and the influence of electric field on the collision probability.

Adiabatic parcel examples
The Parcel environment shares the zero-dimensionality of Box (i.e., no particle physical coordinates considered), yet provides a thermodynamic evolution of the ambient air mimicking adiabatic displacement of an air parcel in hydrostatically stratified atmosphere. Adiabatic cooling during the ascent results in reaching supersaturation what triggers activation of aerosol particles (condensation nuclei) into cloud droplets through condensation. All examples based on the Parcel environment utilise the Condensation and AmbientThermodynamics dynamics.
The simplest example uses a monodisperse particle spectrum represented with a single superdroplet and reproduces simulations described in Arabas & Shima (2017) where an ascent-descent scenario is employed to depict hysteretic behaviour of the activation/deactivation phenomena.
A polydisperse lognormal spectrum represented with multiple super-droplets is used in the example based on the work of Yang et al. (2018).

Kinematic (prescribed-flow) examples
Coupling of PySDM with fluid-flow simulation is depicted with both 1D and 2D prescribed-flow simulations, both dependent on the PyMPDATA package  implementing the MPDATA advection algorithm. For a review on MPDATA, see e.g., Smolarkiewicz (2006).
Usage of the kinematic 1d environment is depicted in an example based on the work of Shipway & Hill (2012), while the kinematic 2d environment is showcased with a Jupyter notebook featuring an interactive user interface and allowing studying aerosol-cloud interactions in drizzling stratocumulus setup based on the work of Arabas et al. (2015).
Figure 2 presents a snapshot from the 2D simulation described in detail in Arabas et al. (2015) and works cited therein. Each plot depicts a 1.5 km by 1.5 km vertical slab of an idealised atmosphere in which a prescribed single-eddy non-divergent flow is forced (updraft in the left-hand part of the domain, downdraft in the right-hand part). The left plot shows the distribution of aerosol particles in the air. The upper part of the domain is covered with a stratocumulus-like cloud which formed on the aerosol particles above the flat cloud base at the level where relative humidity goes above 100%. Within the cloud, the aerosol concentration is thus reduced. The middle plot depicts the sizes of particles. Particles larger than 1 micrometre in diameter are considered as cloud droplets, particles larger than 50 micrometres in diameter are considered as drizzle (unlike in bin or bulk models, such categorisation is employed for analysis only and not within the particle-based model formulation). Concentration of drizzle particles forming through collisions is depicted in the right panel. A rain shaft forms in the right part of the domain where the downward flow direction amplifies particle sedimentation. Precipitating drizzle drops collide with aerosol particles washing out the sub-cloud aerosol.
Most of the drizzle drops evaporate before reaching the bottom of the domain depicting the virga phenomenon and the resultant aerosol resuspension.

Summary
The key goal of the reported endeavour was to equip the cloud modelling community with a solution enabling rapid development and paper-review-level reproducibility of simulations (i.e., technically feasible without contacting the authors and possible to be set up within minutes) while being free from the two-language barrier commonly separating prototype and high-performance research code. The key advantages of PySDM stem from the characteristics of the employed Python language which enables high performance computational modelling without trading off such features as: succinct syntax -the snippets presented in the paper are arguably close to pseudo-code;