krithika

_images/krithika_logo_highres.png

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, matplotlib

  • astropy, astroquery

  • juliet (light curve fitting and juliet plots)

  • 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

Indices and tables