Receptors#
A receptor is a point (or set of points) in space and time that defines where and when STILT releases particles for a backward run. PYSTILT provides three concrete receptor classes that share a common base:
Class |
When to use |
Key attributes |
|---|---|---|
Single measurement location (tower, flask, aircraft point) |
|
|
Vertically-integrated column at one horizontal location (e.g. TCCON) |
|
|
Slant or multi-angle column (e.g. OCO-2, satellite soundings) |
|
PointReceptor#
The most common type. Use it for any fixed surface site or airborne sample at a single height.
import stilt
receptor = stilt.PointReceptor(
time="2023-07-15 18:00:00",
longitude=-111.848,
latitude=40.766,
altitude=10.0, # metres AGL (default) or MSL
)
altitude_ref defaults to "agl" (above ground level). Pass
altitude_ref="msl" for mean-sea-level altitudes.
ColumnReceptor#
Use when the measurement integrates over a vertical range at a single
horizontal position. STILT releases particles at both bottom and top
and the footprints are averaged with weighting supplied externally (e.g. an
averaging kernel).
receptor = stilt.ColumnReceptor(
time="2023-07-15 18:00:00",
longitude=-111.848,
latitude=40.766,
bottom=50.0, # lower altitude (must be < top)
top=6000.0,
altitude_ref="msl",
)
bottom must be strictly less than top; both are validated against
altitude_ref (AGL altitudes must be ≥ 0).
MultiPointReceptor#
Use when the instrument views along a slant path, producing observations at multiple distinct horizontal and vertical positions simultaneously (e.g. OCO-2 soundings, satellite-retrieved columns with a full line-of-sight geometry).
import numpy as np
receptor = stilt.MultiPointReceptor(
time="2023-07-15 18:00:00",
longitudes=np.linspace(-111.85, -111.80, 10),
latitudes=np.linspace(40.76, 40.80, 10),
altitudes=np.linspace(0.0, 8000.0, 10),
altitude_ref="msl",
)
All three coordinate arrays must have the same length. The location identifier is an order-independent SHA-256 hash of the point set, so reordering the points produces the same simulation ID.
Particle distribution#
numpar controls the total number of particles released per simulation.
How those particles are distributed depends on receptor type.
PointReceptor — all numpar particles are released from the single location.
ColumnReceptor — particles are evenly spread across the column from
bottom to top.
MultiPointReceptor — numpar particles are distributed as evenly as
possible across the n release locations. Choosing numpar as a
multiple of n ensures every location receives exactly equal counts.
Loading from CSV#
read_receptors() loads a receptor CSV and returns a list of the
appropriate subclass objects. The CSV format follows the R-STILT convention
(time, longitude/long, latitude/lati, zagl/zmsl).
Rows grouped under the same r_idx are assembled into a
ColumnReceptor or MultiPointReceptor automatically.
receptors = stilt.read_receptors("receptors.csv")
Type dispatch#
Use isinstance() to branch on receptor type in generic code — never
inspect string attributes:
from stilt import ColumnReceptor, MultiPointReceptor, PointReceptor
if isinstance(receptor, PointReceptor):
print(f"Single point at {receptor.altitude} m")
elif isinstance(receptor, ColumnReceptor):
print(f"Column from {receptor.bottom} to {receptor.top} m")
elif isinstance(receptor, MultiPointReceptor):
print(f"Multi-point with {len(receptor)} locations")
All three are subclasses of Receptor, so
isinstance(receptor, stilt.Receptor) is always True for any receptor
object.
Smart constructor#
Receptor.from_points() picks the right subclass from a list of
(longitude, latitude, altitude) tuples:
One tuple →
PointReceptorTwo tuples at the same horizontal location →
ColumnReceptor(bottom/topare sorted automatically)Anything else →
MultiPointReceptor
r = stilt.Receptor.from_points(
time="2023-07-15 18:00:00",
points=[(-111.848, 40.766, 10.0)],
)
# → PointReceptor