krithika
A Python toolkit for exoplanet time‑series analysis across CHEOPS, JWST, TESS, and Kepler/K2, including photometry, spectroscopic light‑curve fitting, and visualization.
Highlights
Aperture photometry for any dataset
CHEOPS data access (DRP light curves, subarrays) and photometry
TESS and Kepler/K2 data access and photometry
Spectroscopic light‑curve analysis with channel binning and parallel fitting
Interactive ND image viewer for 2D/3D/4D datasets
Noise analysis (Allan‑deviation style) and power spectra
PLD/PCA utilities for systematics analysis
Brightness temperature estimation from eclipse depths
Installation
From source
git clone https://github.com/Jayshil/krithika.git
cd krithika
pip install -e .
From pip
pip install krithika
Dependencies (core)
numpy,scipy,matplotlibastropy,astroqueryjuliet(light curve fitting andjulietplots)tqdm,corner
Optional dependencies
photutils(aperture photometry)dace_query(CHEOPS archive access)
Quick Start
ApPhoto (Aperture Photometry)
from krithika import ApPhoto
# Load aperture photometry data (times, frames, errors, bad-pixel map, and aperture information)
## Generating fake data
times = np.arange(1000)
frames, errors = np.ones( (1000, 100, 100) ), np.ones( (1000, 100, 100) )
badpix = np.ones( (1000, 100, 100) ) # 1 means good pixel, 0 means bad-pixel
## In case of circular aperture with sky annulus
phot = ApPhoto(times=times, frames=frames, errors=errors, badpix=badpix, aprad=20, sky_rad1=40, sky_rad2=50)
## In case of aperture made up of N brightest pixels and background made up of M faintest pixels
phot = ApPhoto(times=times, frames=frames, errors=errors, badpix=badpix, brightpix=True, nos_brightest=20, nos_faintest=50)
## Centroids
cenr, cenc, _, _ = phot.find_center()
## Simple aperture photometry
fl, fle, bkg, _, _ = phot.simple_aperture_photometry(method='photutils', bkg_corr='median', robust_err=True, plot=False)
## Pixel-level decorrelation
_, _, PCA = phot.pixel_level_decorrelation()
fl_aper, fl_pred, _, _ = phot.pld_correction()
Please refer to the API section for detailed documentation of all the modules and classes in this package. The ApPhoto class is wrapped around several other classes, CHEOPSData, TESSData, and KeplerData, that allows the users to download CHEOPS, TESS, and Kepler data directly from command lines and use ApPhoto class to perform aperture photometry on these datasets.
CHEOPS: DRP light curves and subarrays
from krithika import CHEOPSData
cheops = CHEOPSData(object_name="WASP-189b")
## This will load DRP light curves
drp = cheops.get_drp_lightcurves(pout="./cheops_data")
## This will download subarrays so that we can then perform aperture photometry (ApPhoto class)
cheops.get_subarrays(pout="./cheops_data")
phot = cheops.ApPhoto(visit_nos=1, aprad=20, sky_rad1=30, sky_rad2=50)
flux, flux_err, _, _, _ = phot.simple_aperture_photometry(method='photutils', bkg_corr='median', robust_err=True, plot=False)
Similarly we can download and work with TESS and Kepler/K2 datasets as follows:
TESS and Kepler/K2 datasets
from krithika import TESSData, KeplerData
## For TESS data
data = TESSData(object_name='WASP-189')
sector = 'TESS51'
## For Kepler data
data = KeplerData(object_name='WASP-107')
sector = 'Kep0000-C10-1'
### To download PDC-SAP light curves
tim, fl, fle, _ = data.get_lightcurves(self, pdc=True)
### To download the target-pixel files
data.get_tpfs()
phot = data.ApPhoto(sector=sector, brightpix=True, nos_brightest=12, nos_faintest=50)
flux, flux_err, _, _, _ = phot.simple_aperture_photometry(method='photutils', bkg_corr='median', robust_err=True, plot=False)
It is also possible to analyse spectroscopic light curves using juliet. Using SpectroscopicLC class, we can fit light curves parallelly. Finally, we can use some of the functions in the class to plot results.
Spectroscopic light‑curve analysis
import numpy as np
import juliet
from krithika import SpectroscopicLC
times = np.load("times.npy")
lc = np.load("spec_lc.npy")
lc_errs = np.load("spec_lc_err.npy")
wavelengths = np.load("wavelengths.npy")
def get_priors(ch_name):
par = ['P_p1', 't0_p1', 'p_p1_' + ch_name, 'b_p1', 'q1_' + ch_name, 'q2_' + ch_name, 'a_p1', 'ecc_p1', 'omega_p1']
dist = ['fixed', 'fixed', 'uniform', 'fixed', 'uniform', 'uniform', 'fixed', 'fixed', 'fixed']
hypers = [1., 0., [0.,1.], 0., [0., 1.], [0., 1.], 10., 0., 90.]
return juliet.utils.generate_priors(par, dist, hypers)
spec = SpectroscopicLC(
times=times,
lc=lc,
lc_errs=lc_errs,
wavelengths=wavelengths,
priors=get_priors,
pout="./results"
)
## Just plotting the 2D data
plot2Ddata(self, cmap='plasma')
plt.show()
## Parallel fitting of the spectroscopic light curves
spec.analyse_lc_parallel(nthreads=4, ch_nos=10)
fig, ax = spec.plot_parameter_spectrum("p_p1", bins=10, plot_white=True)
plt.show()
## Plot the 2D data and model
plot2D_data_model_resids(cmap='plasma')
plt.show()
ND Image Viewer
from krithika import NDImageViewer
import numpy as np
data = np.random.rand(50, 128, 128)
viewer = NDImageViewer(data=data, cmap="magma")
viewer.show()
Plots for juliet-fitting
Finally, using julietPlots class, we can plot light curve models, phase folded light curves, gp models, corner plots, and allan deviation proxy plots for juliet-fitted models.
from krithika import julietPlots
import os
data = julietPlots(input_folder=os.getcwd(), N=5000, sampler='dynamic_dynesty')
## full model with data
fig, ax1, ax2 = data.full_model_lc(['TESS31'], quantile_models=False)
plt.show()
## Phase-folded model
figs, axs1, axs2, axs3, axs4 = data.phase_folded_lc(phmin=0.8, nrandom=30, highres=True, quantile_models=True)
plt.tight_layout()
plt.show()
## To plot GP model
fig, axs = data.plot_gp(instruments=None, highres=True, one_plot=True, pycheops_binning=False)
plt.tight_layout()
plt.show()
## "Allan" deviation plots
fig, axs, _, _, _ = data.plot_fake_allan_deviation(instruments=['TESS31'], method='pipe')
plt.show()
data.plot_corner(planet_only=False, save=True)
Inverting Cowan & Agol (2008) phase curve model
If we used Cowan & Agol (2008) phase curve model, then we can use InvertCowanAgolPC class to invert the fitted phase curve to find thermal physical properties of the planet as shown below:
import numpy as np
from astropy import units as u
from krithika import InvertCowanAgolPC
# Load posterior samples from eclipse depths and phase-curve fitting
E = np.random.normal(0.01, 0.001, 1000) # Eclipse depths (F_p/F_*)
C1 = np.random.normal(0.005, 0.0005, 1000) # Phase-curve cosine (first harmonic)
D1 = np.random.normal(0.002, 0.0002, 1000) # Phase-curve sine (first harmonic)
C2 = np.random.normal(0.001, 0.0001, 1000) # Phase-curve cosine (second harmonic)
D2 = np.random.normal(0.0005, 0.00005, 1000) # Phase-curve sine (second harmonic)
rprs = 0.1 # Planet-to-star radius ratio
# Define instrument bandpass
bandpass = {
'WAVE': np.linspace(0.3, 5.0, 100) * u.um,
'RESPONSE': np.ones(100)
}
# Create inversion object
invert = InvertCowanAgolPC(
E=E, C1=C1, D1=D1, C2=C2, D2=D2,
rprs=rprs,
bandpass=bandpass,
teff_star=5800 * u.K,
pout="./phase_curve_results"
)
## Day/night brightness temperatures
T_day, T_night = invert.TdayTnight()
## Temperature maps across posterior samples
temp_maps = invert.temperature_map_distribution(nsamples=2000)
## Median temperature map
fig, ax = invert.median_temperature_map(plot=True, cmap='plasma')
plt.show()
## Equatorial temperature profile
fig, ax = invert.equatorial_temp_map(plot=True, nsamples=2000)
plt.show()
## Bond albedo and heat redistribution efficiency
a_by_Rst = 10.0 # Semi-major axis in units of stellar radius
A_B, eps_kelp, _, _, _ = invert.albedo_eps_from_temp_map(a_by_Rst)
## Phase offsets (hotspot shift and phase shift)
phi_off, phi_off_err, phase_off, phase_off_err = invert.phase_offsets(method='root')
Selecting linear detrending regressors
The LinearRegressorSelector class allows users to perform forward stepwise model selection of linear detrending regressors based on log-evidence or scatter in the residuals. The user can provide a list of potential linear regressors (e.g., roll angle sinusoids, background flux, centroid positions) and the class will iteratively add the regressor that improves the model fit the most at each step.
from krithika import SelectLinDetrend
import numpy as np
# Prepare data per instrument
time = {'CHEOPS1': np.linspace(0, 100, 1000), 'CHEOPS2': np.linspace(0, 100, 1000)}
flux = {'CHEOPS1': np.random.randn(1000) * 0.001 + 1, 'CHEOPS2': np.random.randn(1000) * 0.002 + 1}
flux_err = {'CHEOPS1': np.ones(1000) * 0.001, 'CHEOPS2': np.ones(1000) * 0.002}
roll_angle = {'CHEOPS1': np.random.uniform(0,360,1000), 'CHEOPS2': np.random.uniform(0,360,1000)}
# Define priors
def get_priors(instrument):
## juliet compatible priors
par = ['P_p1', 't0_p1', 'p_p1', 'b_p1', 'q1_' + instrument, 'q2_' + instrument, 'ecc_p1', 'omega_p1', 'a_p1']
dist = ['fixed', 'fixed', 'uniform', 'fixed', 'uniform', 'uniform', 'fixed', 'fixed', 'fixed']
hypers = [1.0, 0.0, [0., 0.1], 0.0, [0., 1.], [0., 1.], 0., 90., 10.]
par = par + ['GP_sigma_' + ins, 'GP_rho_' + ins]
dist = dist + ['loguniform', 'loguniform']
hypers = hypers + [[1e-3, 1e2], [1, 500]]
return par, dist, hypers
# Define linear regressors
linear_regressors = {
'CHEOPS1': {'time': time['CHEOPS1'], 'time_sq': time['CHEOPS1']**2},
'CHEOPS2': {'time': time['CHEOPS2'], 'time_sq': time['CHEOPS2']**2}
}
# Create selector and run optimization
selector = SelectLinDetrend(
time=time,
flux=flux,
flux_err=flux_err,
priors=get_priors,
linear_regressors=linear_regressors,
roll_degree=3,
roll=roll_angle,
pout="./detrend_results"
)
# Find optimal regressors (using lnZ -- Bayesian evidence -- as a selection criteria)
selected_regressors = selector.select_optimal_parameters(n_parallel=4, delta_lnZ_threshold=2.0, selection_method='lnZ')
# Find optimal regressors (using scatter in the residuals as a selection criteria)
selected_regressors = selector.select_optimal_parameters(n_parallel=4, selection_method='scatter')
Modules & Classes
Please refer to the API section for detailed documentation of all the modules and classes in this package.
Citation
If you use this package in a publication, please cite the repository:
@software{krithika,
author = {Jayshil},
title = {Krithika: Exoplanet Time-Series Analysis Toolkit},
year = {2026},
url = {https://github.com/Jayshil/krithika}
}
API