:code:`krithika` ================ .. image:: ../krithika_logo_highres.png :align: center :width: 700 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 ----------- .. code-block:: bash git clone https://github.com/Jayshil/krithika.git cd krithika pip install -e . From pip -------- .. code-block:: bash 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) ----------------------------- .. code-block:: python 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 :ref:`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 -------------------------------------- .. code-block:: python 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 --------------------------- .. code-block:: python 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 ---------------------------------- .. code-block:: python 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 --------------- .. code-block:: python 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. .. code-block:: python 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: .. code-block:: python 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. .. code-block:: python 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 :ref:`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: .. code-block:: bibtex @software{krithika, author = {Jayshil}, title = {Krithika: Exoplanet Time-Series Analysis Toolkit}, year = {2026}, url = {https://github.com/Jayshil/krithika} } .. toctree:: :maxdepth: 1 :caption: API api.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search`