Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

injectr

python signal signal license

Injects a synthetic transit/EB/blend/starspot signal into a real, already-processed light curve — the noise stays real, only the signal is synthetic. Its whole purpose is producing ground-truth-labeled data for injection-recovery testing: run your pipeline on injectr's output and measure whether the known injected class/parameters come back out, where recovery starts failing as a function of SNR/depth/period, and what your false-positive rate looks like when nothing was injected at all.

This is deliberately different from tools that build fixtures from a synthetic time grid plus synthetic noise from scratch. Real stellar noise (red noise, gaps, systematics, whatever your real target actually looks like) is a much harder and more honest canvas than white noise drawn from a Gaussian — the same "real data over synthetic for fixtures" principle, extended to injection-recovery.

Install

injectr isn't published to PyPI — install straight from GitHub:

pip install git+https://github.com/nikhilcherry/injectr

Never pip install injectr — the bare name isn't ours on PyPI and may be squatted.

Quickstart

Python API

import injectr

result = injectr.inject_planet(
    base_path="data/processed/null/tic123.npz",
    period=5.2, rp=0.05, t0=0.3, a=15.0, inc=89.0,
    seed=42,
)
result.time            # base file's own time array, unchanged
result.flux            # base flux * transit model, real noise preserved
result.flux_err        # copied from the base file unchanged
result.label            # "planet"
result.injection_params  # {"class": "planet", "period": 5.2, "rp": 0.05, "t0": 0.3,
                          #  "a": 15.0, "inc": 89.0, "u": [0.4, 0.25]}
result.injected_depth_ppm       # measured from the model's own minimum flux
result.injected_duration_hours  # analytic T14 duration

result.to_npz("data/injected/planet_0001.npz")
# writes a schema-1.0-compliant file: augmented=True, injection_params=<dict above>,
# label="planet", time/flux/flux_err from the injection, tic_id/sector/mission carried
# over from the base file unchanged.

The equivalent functions for the other three classes have the same shape:

injectr.inject_eb(base_path, period, rp, t0, a, inc, secondary_scale, ecc=0.0, w=90.0, seed=...)
injectr.inject_blend(base_path, period, rp, t0, a, inc, dilution, ecc=0.0, w=90.0, seed=...)
injectr.inject_starspot(base_path, prot, amp1, amp2, phase1, phase2, seed=...)

inject_blend's forward model is the planet model with an extra free dilution parameter: flux = 1 - dilution * (1 - transit_flux). inject_starspot has no eclipse, so injected_depth_ppm/injected_duration_hours are None for that class.

ecc/w (eccentricity, argument of periastron in degrees) default to 0.0/90.0 (circular) and are available on inject_planet/inject_eb/inject_blend and their --ecc/--w CLI equivalents. injected_duration_hours applies the standard eccentric-orbit T14 correction (Winn 2010 eq. 16) automatically when ecc != 0, so non-circular injections still get a correct ground-truth duration.

No noise is added beyond the base file's own — that real noise is the noise. Pass extra_noise_ppm=<value> to any inject_* call (or --extra-noise-ppm on the CLI) to opt into extra Gaussian jitter on top; it's 0.0 (off) by default.

CLI

# single injection
injectr inject data/processed/null/tic123.npz --class planet \
  --period 5.2 --rp 0.05 --t0 0.3 --a 15.0 --inc 89.0 \
  --seed 42 --output data/injected/planet_0001.npz --json

# batch: draw N injections per class from a param-grid YAML, spread across
# base files listed in a manifest CSV; writes an injection_manifest.csv
injectr batch --base-manifest base_manifest.csv --classes planet,eb,blend \
  --grid grid.yaml --n-per-class 200 --seed 42 --output-dir data/injected/ \
  --output-manifest data/injected/injection_manifest.csv

injectr batch exits 0 on success. Any output_path that already exists is skipped rather than recomputed, so a killed run resumes cleanly — see Batch and resumability below.

Base files

A base file is a schema-1.0 .npz — ideally a quiet/null-labeled target's real, already-processed photometric light curve. Use quiet targets as the injection canvas so the "ground truth" signal you're testing recovery of isn't confounded by a real signal already present in the base file. injectr evaluates its forward model on the base file's own time array and combines it multiplicatively with the base file's own flux; flux_err is carried over unchanged. injectr does no detrending or renormalization of the base flux — whatever's in the base file's flux array is what gets multiplied.

Batch and resumability

--base-manifest is a CSV with a path column (falls back to the first column) listing base .npz files, e.g.:

path
data/processed/null/tic123.npz
data/processed/null/tic456.npz

--grid is a YAML mapping of class name to parameter grid, where each parameter is either a [min, max] numeric range (drawn uniformly), a list of discrete choices, or a fixed scalar:

planet:
  period: [1.0, 10.0]
  rp: [0.02, 0.15]
  t0: [0.0, 1.0]
  a: [5.0, 30.0]
  inc: [85.0, 90.0]
eb:
  period: [0.5, 5.0]
  rp: [0.05, 0.2]
  t0: [0.0, 1.0]
  a: [4.0, 15.0]
  inc: [80.0, 90.0]
  secondary_scale: [0.1, 0.5]
blend:
  period: [1.0, 10.0]
  rp: [0.02, 0.15]
  t0: [0.0, 1.0]
  a: [5.0, 30.0]
  inc: [85.0, 90.0]
  dilution: [0.2, 0.8]
starspot:
  prot: [1.0, 15.0]
  amp1: [0.005, 0.03]
  amp2: [0.0, 0.01]
  phase1: [0.0, 6.283]
  phase2: [0.0, 6.283]

t0 is in the same time units as the base file's time array (days from whatever epoch the base file uses) — it just sets the injected transit's phase reference, so any value works even outside the observed baseline.

injectr batch draws are round-robin spread across a shuffled copy of the base-manifest paths, and every draw gets its own seed derived deterministically from --seed plus its class/index — so a batch run is fully reproducible end to end. Resumability here is just "skip if output_path already exists"; it is not batchr's content-hash cache. If you want real parallel execution with content-hash caching, don't reimplement that here — wrap injectr.inject_* with batchr.run_batch directly:

from batchr import run_batch
import injectr

def inject_one(row: dict) -> dict:
    result = injectr.inject_planet(row["base_path"], **row["params"], seed=row["seed"])
    result.to_npz(row["output_path"])
    return {"label": result.label, "depth_ppm": result.injected_depth_ppm}

report = run_batch(inject_one, rows, cache_dir=".injectr-cache")

The locked limb-darkening constant

injectr.models.LD_COEFFS = [0.4, 0.25] is fixed to match fitr/models/planet.py's own fixed quadratic limb-darkening constant exactly. If you inject a transit with different LD coefficients, a downstream fitter using fitr's fixed u=[0.4, 0.25] carries a small but systematic shape residual that its blend model can partially absorb via its extra free dilution parameter — the verdict stays confident but the winning class becomes blend instead of planet, even though the injected ground truth is planet (see arvyo-pipeline/scripts/regenerate_fixtures.py's module docstring, which hit this bug empirically before pinning the same constant there). Every inject_planet/inject_eb/ inject_blend call uses LD_COEFFS and records it in injection_params["u"].

Schema-1.0

injectr/contract.py is injectr's own copy of the schema-1.0 constants (required/optional arrays and metadata, valid labels). This is a manual sync point with arvyo-pipeline/arvyo/contract.py, not a shared import — injectr is a standalone, git-installable tool and does not depend on arvyo-pipeline's internals. If schema-1.0 ever changes, both copies need to be updated by hand.

Non-goals for v1

  • No dependency on arvyo-pipeline's internal forward models — independent reimplementation only.
  • No recovery scoring or pipeline execution. injectr only produces labeled data; scoring whether it was recovered is the downstream pipeline's job.
  • No re-detrending or renormalization of base flux.
  • No parallel execution built in — wrap with batchr (see above).
  • No vendoring of third-party code. batman stays a pip dependency (batman-package, GPL-3.0) exactly as it already is project-wide.

Development

pip install -e ".[dev]"
pytest

About

No description or website provided.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages