panoptes.pipeline.lightcurve.injection¶
panoptes.pipeline.lightcurve.injection ¶
Transit injection and recovery.
The only honest way to show an algorithm change helped is to inject a known signal and measure how much of it comes back. Self-subtraction is a real risk here: the comparison star is assembled from stars chosen for their similarity to the target over the whole sequence, in-transit frames included, so a deep enough transit can partially reproduce itself in its own comparison.
Flux marginalization (core.normalize_psc) protects against most of this by construction, but "mostly" is not a number. These helpers produce the number.
RecoveryResult
dataclass
¶
Outcome of one injection/recovery trial.
Source code in src/panoptes/pipeline/lightcurve/injection.py
suppression
property
¶
Fraction of the injected depth lost to the algorithm. 0.0 is perfect.
Positive values mean the comparison star partly absorbed the signal. This is the number that decides whether a reference-selection change is safe: references are chosen by similarity across every frame, in-transit frames included, so nothing but flux marginalization stops the comparison from learning the transit.
significance
property
¶
Recovered depth in units of the out-of-transit scatter per point.
box_transit ¶
Sharp-edged transit model. 1.0 out of transit, 1 - depth inside.
Source code in src/panoptes/pipeline/lightcurve/injection.py
trapezoid_transit ¶
trapezoid_transit(times: ndarray, mid_transit: float, duration_hours: float, depth: float, ingress_fraction: float = 0.15) -> np.ndarray
Trapezoidal transit with linear ingress and egress ramps.
Closer to a real limb-darkened transit than a box while staying analytic.
ingress_fraction is the ramp length as a fraction of total duration.
Source code in src/panoptes/pipeline/lightcurve/injection.py
inject ¶
Scale every pixel of each frame by the transit model.
Injection happens at the pixel level, before normalization and reference selection, so the full algorithm sees the signal exactly as it would see a real one. Injecting into the finished lightcurve instead would measure nothing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
psc
|
ndarray
|
|
required |
model
|
ndarray
|
|
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
Source code in src/panoptes/pipeline/lightcurve/injection.py
measure_depth ¶
Compare in-transit and out-of-transit levels of a recovered lightcurve.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flux
|
ndarray
|
|
required |
model
|
ndarray
|
|
required |
threshold
|
float
|
Frames where the model dips below this fraction of full depth count as in-transit. Frames on the ramps are excluded from both samples so partial coverage does not dilute the measurement. |
0.5
|
Source code in src/panoptes/pipeline/lightcurve/injection.py
sinusoid ¶
Relative flux model for a sinusoidal variation of known amplitude.
Sinusoids rather than transits, deliberately. Measuring fidelity with a transit shape only tells you about transit-shaped signals; a sweep over period measures what the algorithm does to any variation at that timescale, which is what the algorithm is actually responsible for.
Source code in src/panoptes/pipeline/lightcurve/injection.py
measure_amplitude ¶
Least-squares amplitude of a known-period sinusoid in a lightcurve.
Fits a + b sin(wt) + c cos(wt) and returns sqrt(b^2 + c^2), so the
result is independent of the injected phase.
Source code in src/panoptes/pipeline/lightcurve/injection.py
transfer_function ¶
transfer_function(recover, times: ndarray, periods_hours: tuple[float, ...], amplitude: float = 0.01, num_phases: int = 4) -> dict[float, float]
Fraction of an injected signal that survives the pipeline, by timescale.
This is the algorithm's objective. Its job is to return the target's true relative flux -- nothing suppressed, nothing invented -- and a transfer function of 1.0 at every timescale of interest is what that means quantitatively. Detection is a property of the survey built on top, not of the algorithm, and optimizing the algorithm for a transit shape would bias it toward signals matching that prior and against everything else the data contains.
A long-timescale rolloff is the failure mode to watch for: a model with many free parameters fitted across a whole sequence will absorb slow variation, and a transit-shaped test at one duration can miss it entirely.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
recover
|
|
required | |
times
|
ndarray
|
Observation times. |
required |
periods_hours
|
tuple[float, ...]
|
Timescales to probe. |
required |
amplitude
|
float
|
Injected amplitude, small enough to stay in the linear regime. |
0.01
|
num_phases
|
int
|
Injections per period, averaged, to remove phase sensitivity. |
4
|
Returns:
| Type | Description |
|---|---|
dict[float, float]
|
|