Total emission¶
The basin total emitted mass from a TMTPP posterior, as posterior samples split into detected and inferred-missing mass, with the mass-rate over the observed windows. M_total = Λ E_f[Q] D needs a mean event duration D in the reciprocal of the flux's time unit, so the mass is in kg. The rate is M_total / T_eff, with T_eff the length of the union of the observed windows, the same windows Λ integrates over. Tier V.
totals
¶
Total-emission estimator (implementation in plumax.population._src.totals).
TotalEmission
dataclass
¶
TotalEmission(samples_kg: Float[Array, ' member'], detected_kg: Float[Array, ' member'], missing_kg: Float[Array, ' member'], t_effective_s: float)
Posterior samples of a total emission, split into detected and missing mass.
samples_kg = detected_kg + missing_kg draw by draw.
Attributes:
| Name | Type | Description |
|---|---|---|
samples_kg |
Float[Array, ' member']
|
M_total, the un-thinned total mass [kg]. |
detected_kg |
Float[Array, ' member']
|
Λ E_f[Q P_d(Q)] D, the mass the model expects to be detected [kg]. |
missing_kg |
Float[Array, ' member']
|
Λ E_f[Q (1 − P_d(Q))] D, the inferred missing mass [kg]. |
t_effective_s |
float
|
T_eff, the length of the union of the observed windows [s]. |
rate_tg_per_yr
¶
Mean emission rate while observed, M_total / T_eff [Tg yr⁻¹].
Returns:
| Type | Description |
|---|---|
Float[Array, ' member']
|
Posterior samples of the rate, over a Julian year. |
Source code in src/plumax/population/_src/totals.py
total_emission
¶
total_emission(posterior: TMTPPPosterior | Mapping[str, TMTPPPosterior], *, horizon: tuple[float, float] | Sequence[tuple[float, float]], event_duration: float, log_pod: Callable[[Array], Array] | None, q_min: float, q_max: float, n_nodes: int = 2001, nodes_per_day: int = NODES_PER_DAY, pods: Sequence[InstrumentPOD] | None = None, tod_origin: float = 0.0) -> TotalEmission
Posterior samples of the basin total, with the inferred missing mass.
M_total⁽ᵉ⁾ = Λ⁽ᵉ⁾ E_f⁽ᵉ⁾[Q] D, M_missing⁽ᵉ⁾ = Λ⁽ᵉ⁾ E_f⁽ᵉ⁾[Q (1 − P_d(Q))] D.
E_f[Q] is the mark's truncated_mean, the partial first moment over
[q_min, q_max], not a mean conditional on that range. See the module docstring for
the mass and mass-rate convention.
Algorithm
- Merge
horizoninto disjoint observed windows; T_eff is their length. - For each draw, Λ over those windows, from the kernel's closed form or Simpson's rule.
- For each draw, E_f[Q] in closed form and E_f[Q (1 − P_d)] by log-space Simpson over [q_min, q_max].
- Detected mass is the difference, floored at zero against round-off.
- Sum strata draw by draw: their posteriors are independent, so any pairing of their draws is a draw of the sum.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
posterior
|
TMTPPPosterior | Mapping[str, TMTPPPosterior]
|
A TMTPP posterior, or one per stratum (the design's v1 spatial shortcut: facility classes that sum to the basin). |
required |
horizon
|
tuple[float, float] | Sequence[tuple[float, float]]
|
The observed window |
required |
event_duration
|
float
|
Mean event duration D > 0, in the reciprocal of Q's time unit. |
required |
log_pod
|
Callable[[Array], Array] | None
|
log P_d on rates in the marks' units, as
:func: |
required |
q_min
|
float
|
Lower limit of the mark integrals, > 0. |
required |
q_max
|
float
|
Upper limit of the mark integrals, finite and > |
required |
n_nodes
|
int
|
Simpson nodes of the missing-mass integral. |
2001
|
nodes_per_day
|
int
|
Quadrature density of Λ for kernels without a closed form. Lower it for long windows when the kernel is smooth. |
NODES_PER_DAY
|
pods
|
Sequence[InstrumentPOD] | None
|
The instruments of a fit with a calibration prior; requires
|
None
|
tod_origin
|
float
|
Local hour of day at t = 0 [hours], for |
0.0
|
Returns:
| Name | Type | Description |
|---|---|---|
The |
TotalEmission
|
class: |
Raises:
| Type | Description |
|---|---|
ValueError
|
For an empty window, invalid mark limits or duration, no strata,
strata with different numbers of draws, or both |
Examples:
Two draws of a constant rate, where a tenth of the events are detected:
>>> import math
>>> import jax.numpy as jnp
>>> from plumax.population.fitting import TMTPPPosterior
>>> from plumax.population.marks import LognormalMark
>>> samples = {
... "lambda_0": jnp.array([1.0, 2.0]),
... "mark.mu": jnp.zeros(2),
... "mark.sigma": jnp.ones(2),
... }
>>> posterior = TMTPPPosterior("constant", LognormalMark(), samples)
>>> total = total_emission(
... posterior,
... horizon=(0.0, 10.0),
... event_duration=2.0,
... log_pod=lambda q: math.log(0.1) + jnp.zeros_like(q),
... q_min=1e-6,
... q_max=1e6,
... )
>>> # M_total = λ T e^{1/2} D
>>> expected = jnp.array([1.0, 2.0]) * 10.0 * math.exp(0.5) * 2.0
>>> bool(jnp.allclose(total.samples_kg, expected))
True
>>> bool(jnp.allclose(total.detected_kg, 0.1 * expected, rtol=1e-4))
True
>>> total.t_effective_s
864000.0
Source code in src/plumax/population/_src/totals.py
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | |