Particula
July 28, 2026 · View on GitHub
What is Particula?
Particula is an open-source, Python-based aerosol simulator that bridges experimental data with computational models. It captures gas-particle interactions, transformations, and dynamics to power predictive aerosol science—so you can uncover deeper insights and accelerate progress.
Why Use Particula?
Aerosols influence atmospheric science, air quality, and human health in powerful ways. Gaining insight into how they behave is essential for effective pollution control, accurate cloud formation modeling, and safer indoor environments. Particula provides a robust, flexible framework to simulate, analyze, and visualize aerosol processes with precision—empowering you to make breakthroughs and drive impactful science.
How Does Particula Help You?
Whether you’re a researcher, educator, or industry expert, Particula is designed to empower your aerosol work by:
- Harnessing ChatGPT integration for real-time guidance, troubleshooting, and Q&A, here.
- Providing a Python-based API for reproducible and modular simulations.
- Building gas-phase properties with builder/factory patterns (vapor pressure and latent heat) that support unit-aware setters and exports.
- Inspecting fixed particle-resolved slots with the read-only CPU
get_slot_diagnostics()API, which reports deterministic free-slot indices and per-box active/free counts without changingParticleData. - Activating fixed particle-resolved slots with the CPU-only direct import
from particula.particles.slot_management import activate_slots. It maps each declared request prefix into ascending free slots, performs all validation before mutating mass, concentration, or charge, and returns fresh per-boxnp.int32activation counts. It is not exported throughparticula.particlesand does not resize storage. For caller-managed Warp storage, importactivate_slots_gpufromparticula.gpu.kernels. This P4 boundary maps selected request-prefix records to ascending free slots in place and returns suppliedwp.int32activation/diagnostic sidecars. Particle/request storage must be same-devicewp.float64; it performs no implicit transfer. Preflight validates metadata, aliasing, state, counts, capacity, and selected records before its writer launches, so rejected calls preserve accessible caller-owned arrays. Rollback is not promised after a writer launch, and concrete-module-onlyget_slot_diagnostics_gpuremains outside the package exports. - Supporting CPU latent-heat-corrected condensation diagnostics with
thermal resistance, latent-heat mass transfer rate utilities,
latent-heat energy-density bookkeeping, and the
CondensationLatentHeatstrategy with latent-heat-correctedmass_transfer_rate()/rate()plus astep()that tracks per-step latent heat diagnostics. The bounded, low-level direct GPU condensation path optionally applies a latent-rate correction during each of its four equal substeps, with deterministic fp64 CPU-oracle/Warp parity coverage. Omitted latent heat, or a zero per-species value, retains that species' isothermal rate path. The direct hook couples each P2-finalized particle transfer to gas using particle concentration and has separate particle-mass, gas- concentration, and per-box/per-species inventory-conservation regressions. Broader temperature feedback and CPU-strategy/runnable-level support remain deferred; this bounded direct-kernel behavior does not establish those broader contracts. - Interrogating your experimental data to validate and expand your impact.
- Documenting explicit direct-GPU process calls with the
complete-process source.
It performs one explicit conversion per CPU container and one final
synchronization/restore;
it is illustrative rather than a backend selector, scheduler, resident loop,
Runnable, or CPU fallback. - Fostering open-source collaboration to share ideas and build on each other’s work.
Join the Community
We welcome contributions from scientists, developers, and students—and anyone curious about aerosol science! Whether you’re looking to ask questions, get help, or contribute fresh ideas, you’ve come to the right place.
Get more by posting on GitHub Discussions and tag any of the contributors using @github-handle.
- 💬 Ask questions and get help.
- 🚀 Share your research with the community to inspire others.
- 📣 Give us feedback.
- 🌟 Contribute to Particula by submitting pull requests or reporting issues on GitHub.
- 🔗 Read our Contributing Guide to learn how you can make an impact.
We’re excited to collaborate with you! ✨
Cite Particula in Your Research
Particula [Computer software]. DOI: 10.5281/zenodo.6634653
Get Started with Particula
Setup Particula{ .md-button .md-button--primary } API Reference{ .md-button } Examples{ .md-button } Theory{ .md-button }
:simple-pypi: PyPI Installation
If your Python environment is already set up, install Particula directly from PyPI:
pip install particula
:simple-condaforge: Conda Installation
Alternatively, you can install Particula using conda:
conda install -c conda-forge particula
If you are new to Python or plan on going through the Examples, head to Setup Particula for more comprehensive installation instructions.
Quick Start Example
This “Quick Start Example” demonstrates a concise workflow for building an aerosol system in Particula and performing a single condensation step.
import numpy as np
import particula as par
# 1. Build the GasSpecies for an organic vapor:
organic = (
par.gas.GasSpeciesBuilder()
.set_name("organic")
.set_molar_mass(180e-3, "kg/mol")
.set_vapor_pressure_strategy(
par.gas.ConstantVaporPressureStrategy(1e2) # Pa
)
.set_partitioning(True)
.set_concentration(np.array([1e2]), "kg/m^3")
.build()
)
# 2. Use AtmosphereBuilder to configure temperature, pressure, and species:
atmosphere = (
par.gas.AtmosphereBuilder()
.set_temperature(298.15, "K")
.set_pressure(101325, "Pa")
.set_more_partitioning_species(organic)
.build()
)
# 3. Build the particle distribution:
# Using PresetParticleRadiusBuilder, we set mode radius, GSD, etc.
particle = (
par.particles.PresetParticleRadiusBuilder()
.set_mode(np.array([100e-9]), "m")
.set_geometric_standard_deviation(np.array([1.2]))
.set_number_concentration(np.array([1e8]), "1/m^3")
.set_density(1e3, "kg/m^3")
.build()
)
# 4. Create the Aerosol combining the atmosphere and particle distribution:
aerosol = (
par.AerosolBuilder()
.set_atmosphere(atmosphere)
.set_particles(particle)
.build()
)
# 5. Define the isothermal condensation strategy:
condensation_strategy = par.dynamics.CondensationIsothermal(
molar_mass=180e-3, # kg/mol
diffusion_coefficient=2e-5, # m^2/s
accommodation_coefficient=1.0,
)
# 6. Build the MassCondensation process:
process = par.dynamics.MassCondensation(condensation_strategy)
# 7. Execute the condensation process over 10 seconds:
result = process.execute(aerosol, time_step=10.0)
# The result is an Aerosol instance with updated particle properties.
print(result)
Feature deep-dives
-
Condensation strategy system — strategy-based condensation (simultaneous and staggered theta modes) with runnable pipelines.
-
Wall loss strategy system — chamber wall loss strategies with builders, factory, and runnable integration.
-
CPU dilution strategy system — construct supported processes with
par.dynamics.DilutionStrategy(coefficient)andpar.dynamics.Dilution(strategy). The concrete helper boundary remains:dilute_aerosolandget_dilution_stepare concrete-module-only helpers, notparticula.dynamicspublic APIs. Run the public API source example withpython docs/Examples/cpu_dilution.py. -
Direct GPU nucleation is a bounded low-level step imported with
from particula.gpu.kernels import nucleation_step_gpu. The CPU-only publicparticula.dynamics.Nucleationrunnable is separate and is not a GPU fallback. Configuration, records, and caller-owned sidecars remain concrete-only underparticula.gpu.kernels.nucleation; callers explicitly transfer state, select a Warp device, and synchronize before host inspection. The step has no hidden transfer, CPU fallback, resize/compaction, GPURunnable, scheduler/backend integration, or performance guarantee. See the nucleation contract and direct-Warp example. -
GPU dilution P1–P4 is a direct, low-level operation imported with
from particula.gpu.kernels import dilution_step_gpu. It appliesc_new = c * exp(-alpha * time_step)in place to particle and gas concentrations, wherealpha = Q / V[s^-1], returns the identical containers, and preserves all other caller-owned fields. Callers own CPU↔Warp transfers, device placement, and synchronization; there is no hidden transfer or CPU fallback. Coefficients are finite nonnegative scalars or same-devicewp.float64Warp arrays shaped(n_boxes,). Deterministic, read-only preflight may run validation scans that allocate or launch, but invalid calls have no update-kernel launch or caller mutation. Scalar-zero coefficients and zero time steps complete preflight and are write-free, no-update-kernel no-ops; validation scans may still allocate or launch. Warp CPU float64 particle and gas comparisons usertol=1e-12, atol=0; CUDA is optional and skips cleanly when unavailable. This is tolerance-based evidence, not bitwise parity. GPU runnables, orchestration, resizing, graph capture, autodiff, and performance claims remain deferred. See Data containers and GPU foundations for the complete contract. -
GPU resampling is a direct fixed-capacity equal-weight remapping operation imported with
from particula.gpu.kernels import resampling_step_gpu. It consumes already-resolved per-box release counts and caller-owned Warp data; read-only preflight and planning diagnostics must succeed before one commit mutates particle state. It has no policy resolution, CPU fallback or transfers, resizing, orRunnablewrapper. The requiredResamplingBuffersrecord is deliberately concrete-module-only atparticula.gpu.kernels.exhaustion, so it is not a general top-level API or a reusable CPU plan. Zero-demand boxes are write-free; planning failure skips the commit and preserves particles, while rollback after commit launch is not promised. -
GPU wall loss is a direct, caller-managed fixed-capacity slot boundary imported with
from particula.gpu.kernels import wall_loss_step_gpu. ConstructNeutralWallLossConfigonly fromparticula.gpu.kernels.wall_loss; it is deliberately not re-exported fromparticula.gpu.kernelsorparticula.gpu. It supports particle-resolved neutral and charged configurations. Inputs use SI units: wall eddy diffusivity [m²/s], radius or dimensions [m], temperature [K], pressure [Pa], time [s], signed wall potential [V], and electric field [V/m]. Charged spherical execution preserves a signed scalar field and adds the signed potential-derived field. Charged rectangular execution accepts a caller-owned, same-devicewp.float64field shaped(3,), reduces its lanes to their Euclidean magnitude, then adds the signed potential-derived field; component signs do not select drift direction. Nonzero-charge slots retain image-charge enhancement whenwall_potential=0; zero-charge charged slots use the exact neutral coefficient and RNG path. Callers own particle charge, optional(n_boxes,)wp.uint32RNG state, explicit transfers, device placement, and synchronization. After read-only preflight, selected eligible slots have all mass lanes, concentration, and charge cleared in place, while fixed capacity and unselected storage are preserved. Rejected pre-launch calls do not mutate caller-owned state; rollback is not promised after launch. Warp CPU is the baseline when available, and CUDA is optional with clean skips:pytest particula/gpu/dynamics/tests/wall_loss_funcs_test.py -q -Werror pytest particula/gpu/kernels/tests/wall_loss_test.py \ particula/gpu/kernels/tests/wall_loss_parity_test.py -q -WerrorHidden transfers or CPU fallback, a GPU runnable or scheduler/backend integration, dynamic slots, graph capture, differentiability, exact cross-backend RNG replay, mandatory CUDA, and performance claims are deferred.
-
Data containers and GPU foundations — canonical reference for
ParticleData,GasData,EnvironmentData, explicit CPU↔GPU transfer helpers, leading-axis shape conventions, the current shipped CPU/GPU support boundary, and caller-owned GPU sidecar state such as coagulationrng_statesand condensation thermodynamics. Direct, particle-resolved GPU coagulation executes singleton masks1,2,4, and8; unordered two-way masks3,5,6,9,10, and12; and four-way mask15across Brownian, charged hard-sphere, SP2016 sedimentation, and ST1956 turbulent shear. Non-turbulent three-way mask7rejects at capability preflight before particle metadata or enabled-term validation. Turbulent three-way masks11,13, and14proceed through particle metadata and enabled-term validation, then reject before downstream normalization, allocation, RNG setup, kernel launch, or mutation. Approved turbulent-shear masks require keyword-only positive, finiteturbulent_dissipation(m²/s³) andfluid_density(kg/m³) Python or NumPy floating scalars, or active-devicewp.float64(n_boxes,)Warp arrays. This is a direct-kernel-only contract: high-levelRunnablesupport, CPU fallback, hidden state transfer, graph capture, performance claims, and broad accuracy claims remain out of scope. GPU condensation requires keyword-onlythermodynamics=ThermodynamicsConfig, validates an active-device binary per-box/speciesgas.partitioningmask before mutation, and runs exactly four equal substeps. Disabled species and zero-concentration particle slots receive no transfer. Each finalized transfer updates particle mass and applies the matching weighted delta togas.concentration, so later substeps read coupled gas inventory. It optionally acceptslatent_heatrate correction and its write-only signedenergy_transferdiagnostic, and refreshes caller-ownedWarpGasData.vapor_pressurefrom the current device-resident temperature before mass transfer. For the supported low-level, direct-kernel-only walkthrough, usepython docs/Examples/gpu_direct_kernels_quick_start.py. This path does not add high-levelAerosol/Runnablesupport, implicit simulation-state transfers or synchronization, automatic fallback or migration, or CPU-strategy/runnable parity. See the GPU condensation command matrix for focused troubleshooting and reproduction commands. Entry-point validation can still perform synchronous device-to-host readbacks. For a focused Brownian route, the GPU coagulation example explicitly transfersParticleData, then makes two concrete, selected-adapter Brownian particle-resolved dispatches with caller-owned collision and persistent RNG sidecars on Warp CPU by default before explicitly restoring CPU particle data. The adapter is concrete-only; see the coagulation strategy guide for its import and capability boundary. When Warp is unavailable or disabled, the example runs no conversion or dispatch and provides no CPU fallback. It makes no public adapter,Runnable, CUDA, or performance claim. Explicit restoration is not checkpoint or restart support; caller state persistence and restart behavior remain deferred. -
ParticleData and GasData migration guide — migration workflow and before/after examples for moving from legacy facades to the canonical data-container contract documented in the foundation guide.
-
Data-oriented design and GPU roadmap — current schema inventory, authoritative CPU/GPU field ownership policy, shipped coagulation RNG ownership and graph-capture setup guidance, and the final downstream handoff map for sibling E2 features.
-
Mass Precision Recommendation Report — final E2-F6 policy report covering deterministic NPF-to-droplet GPU evidence, the accepted unchanged
fp64/wp.float64production baseline, study-only candidate fidelity checks, executable P3 thresholds, clamp accounting, memory-footprint examples, and focused reproduction commands.