| name | box-least-squares |
| description | Box Least Squares (BLS) periodogram for detecting transiting exoplanets and eclipsing binaries. Use when searching for periodic box-shaped dips in light curves. Alternative to Transit Least Squares, available in astropy.timeseries. Based on Kovács et al. (2002). |
Box Least Squares (BLS) Periodogram
The Box Least Squares (BLS) periodogram is a statistical tool for detecting transiting exoplanets and eclipsing binaries in photometric time series data. BLS models a transit as a periodic upside-down top hat (box shape) and finds the period, duration, depth, and reference time that best fit the data.
Overview
BLS is built into Astropy and provides an alternative to Transit Least Squares (TLS). Both search for transits, but with different implementations and performance characteristics.
Key parameters BLS searches for:
- Period (orbital period)
- Duration (transit duration)
- Depth (how much flux drops during transit)
- Reference time (mid-transit time of first transit)
Installation
BLS is part of Astropy:
pip install astropy
Basic Usage
import numpy as np
import astropy.units as u
from astropy.timeseries import BoxLeastSquares
t = time * u.day
y = flux
dy = flux_err
model = BoxLeastSquares(t, y, dy=dy)
duration = 0.2 * u.day
periodogram = model.autopower(duration)
best_period = periodogram.period[np.argmax(periodogram.power)]
print(f"Best period: {best_period:.5f}")
Using autopower vs power
autopower: Automatic Period Grid
Recommended for initial searches. Automatically determines appropriate period grid:
duration = 0.2 * u.day
periodogram = model.autopower(duration)
durations = [0.1, 0.15, 0.2, 0.25] * u.day
periodogram = model.autopower(durations)
power: Custom Period Grid
For more control over the search:
periods = np.linspace(2.0, 10.0, 1000) * u.day
duration = 0.2 * u.day
periodogram = model.power(periods, duration)
Warning: Period grid quality matters! Too coarse and you'll miss the true period.
Objective Functions
BLS supports two objective functions:
1. Log Likelihood (default)
Maximizes the statistical likelihood of the model fit:
periodogram = model.autopower(0.2 * u.day, objective='likelihood')
2. Signal-to-Noise Ratio (SNR)
Uses the SNR with which the transit depth is measured:
periodogram = model.autopower(0.2 * u.day, objective='snr')
The SNR objective can improve reliability in the presence of correlated noise.
Complete Example
import numpy as np
import matplotlib.pyplot as plt
import astropy.units as u
from astropy.timeseries import BoxLeastSquares
data = np.loadtxt('light_curve.txt')
time = data[:, 0] * u.day
flux = data[:, 1]
flux_err = data[:, 3]
model = BoxLeastSquares(time, flux, dy=flux_err)
durations = np.linspace(0.05, 0.3, 10) * u.day
periodogram = model.autopower(durations, objective='likelihood')
max_power_idx = np.argmax(periodogram.power)
best_period = periodogram.period[max_power_idx]
best_duration = periodogram.duration[max_power_idx]
best_t0 = periodogram.transit_time[max_power_idx]
max_power = periodogram.power[max_power_idx]
print(f"Period: {best_period:.5f}")
print(f"Duration: {best_duration:.5f}")
print(f"T0: {best_t0:.5f}")
print(f"Power: {max_power:.2f}")
import matplotlib.pyplot as plt
plt.plot(periodogram.period, periodogram.power)
plt.xlabel('Period [days]')
plt.ylabel('BLS Power')
plt.show()
Peak Statistics for Validation
Use compute_stats() to calculate detailed statistics about a candidate transit:
stats = model.compute_stats(
periodogram.period[max_power_idx],
periodogram.duration[max_power_idx],
periodogram.transit_time[max_power_idx]
)
print(f"Depth: {stats['depth']:.6f}")
print(f"Depth uncertainty: {stats['depth_err']:.6f}")
print(f"SNR: {stats['depth_snr']:.2f}")
print(f"Odd/Even mismatch: {stats['depth_odd'] - stats['depth_even']:.6f}")
print(f"Number of transits: {stats['transit_count']}")
if abs(stats['depth_odd'] - stats['depth_even']) > 3 * stats['depth_err']:
print("Warning: Significant odd-even mismatch - may not be planetary")
Validation criteria:
- High depth SNR (>7): Strong signal
- Low odd-even mismatch: Consistent transit depth
- Multiple transits observed: More reliable
- Reasonable duration: Not too long or too short for orbit
Period Grid Sensitivity
The BLS periodogram is sensitive to period grid spacing. The autoperiod() method provides a conservative grid:
periods = model.autoperiod(durations, minimum_period=1*u.day, maximum_period=10*u.day)
print(f"Period grid has {len(periods)} points")
periodogram = model.power(periods, durations)
Tips:
- Use
autopower() for initial searches
- Use finer grids around promising candidates
- Period grid quality matters more for BLS than for Lomb-Scargle
Comparing BLS Results
To compare multiple peaks:
sorted_idx = np.argsort(periodogram.power)[::-1]
top_5 = sorted_idx[:5]
print("Top 5 candidates:")
for i, idx in enumerate(top_5):
period = periodogram.period[idx]
power = periodogram.power[idx]
duration = periodogram.duration[idx]
stats = model.compute_stats(period, duration, periodogram.transit_time[idx])
print(f"\n{i+1}. Period: {period:.5f}")
print(f" Power: {power:.2f}")
print(f" Duration: {duration:.5f}")
print(f" SNR: {stats['depth_snr']:.2f}")
print(f" Transits: {stats['transit_count']}")
Phase-Folded Light Curve
After finding a candidate, phase-fold to visualize the transit:
phase = ((time.value - best_t0.value) % best_period.value) / best_period.value
import matplotlib.pyplot as plt
plt.plot(phase, flux, '.')
plt.xlabel('Phase')
plt.ylabel('Flux')
plt.show()
BLS vs Transit Least Squares (TLS)
Both methods search for transits, but differ in implementation:
Box Least Squares (BLS)
Pros:
- Built into Astropy (no extra install)
- Fast for targeted searches
- Good statistical framework
- compute_stats() provides detailed validation
Cons:
- Simpler transit model (box shape)
- Requires careful period grid setup
- May be less sensitive to grazing transits
Transit Least Squares (TLS)
Pros:
- More sophisticated transit models
- Generally more sensitive
- Better handles grazing transits
- Automatic period grid is more robust
Cons:
- Requires separate package
- Slower for very long time series
- Less control over transit shape
Recommendation: Try both! TLS is often more sensitive, but BLS is faster and built-in.
Integration with Preprocessing
BLS works best with preprocessed data. Consider this pipeline:
- Quality filtering: Remove flagged data points
- Outlier removal: Clean obvious artifacts
- Detrending: Remove stellar variability (rotation, trends)
- BLS search: Run period search on cleaned data
- Validation: Use
compute_stats() to check candidate quality
Key Considerations
- Preprocessing should preserve transit shapes (use gentle methods like
flatten())
- Don't over-process - too aggressive cleaning removes real signals
- BLS needs reasonable period and duration ranges
- Always validate with multiple metrics (power, SNR, odd-even)
Common Issues
Issue: No clear peak
Causes:
- Transits too shallow
- Wrong duration range
- Period outside search range
- Over-aggressive preprocessing
Solutions:
- Try wider duration range
- Extend period search range
- Use less aggressive
flatten() window
- Check raw data for transits
Issue: Period is 2× or 0.5× expected
Causes:
- Missing alternating transits
- Data gaps
- Period aliasing
Solutions:
- Check both periods manually
- Examine odd-even statistics
- Look at phase-folded plots for both periods
Issue: High odd-even mismatch
Cause:
- Not a planetary transit
- Eclipsing binary
- Instrumental artifact
Solution:
- Check
stats['depth_odd'] vs stats['depth_even']
- May not be a transiting planet
Dependencies
pip install astropy numpy matplotlib
pip install lightkurve
References
Official Documentation
Key Papers
- Kovács, Zucker, & Mazeh (2002): Original BLS paper - A&A 391, 369
- Hartman & Bakos (2016): VARTOOLS implementation - A&C 17, 1
Related Resources
When to Use BLS
Use BLS when:
- You want a fast, built-in solution
- You need detailed validation statistics (
compute_stats)
- Working within the Astropy ecosystem
- You want fine control over period grid
Use TLS when:
- Maximum sensitivity is critical
- Dealing with grazing or partial transits
- Want automatic robust period grid
- Prefer more sophisticated transit models
Use Lomb-Scargle when:
- Searching for general periodic signals (not specifically transits)
- Detecting stellar rotation, pulsation
- Initial exploration of periodicity
For exoplanet detection, both BLS and TLS are valid choices. Try both and compare results!