Euclid Archival Data Analysis Package
Project description
euclidkit
A comprehensive Python package for Euclid archival data analysis, designed for use within the ESA Datalabs environment.
Overview
euclidkit facilitates advanced data exploration and visualization for Euclid Q1/(I)DR1 archival releases, including:
- Data Access: Query and crossmatch sources with the Euclid MER catalogue
- Segmentation Maps: Resolve MER segmentation-map metadata from crossmatch results
- Spectroscopic Analysis: Access, download, and combine NISP spectra of archival sources
- Unified Workflow: Streamlined tools for researchers working with Euclid spectroscopic data
The package is designed for efficient archive querying and Euclid spectrum compilation workflows.
Installation
Requirements
- Python 3.11+
- Access to ESA Datalabs environment (for data volumes)
- COSMOS credentials for Euclid archive access
Basic Installation
pip install euclidkit
Development Installation
git clone https://github.com/rudolffu/euclidkit.git
cd euclidkit
pip install -e .
Quick Start
Setup Credentials
Store credentials in a private file under your home directory and restrict permissions:
mkdir -p ~/.euclidkit
touch ~/.euclidkit/.cred.txt
chmod 600 ~/.euclidkit/.cred.txt
Edit ~/.euclidkit/.cred.txt manually with your preferred editor (do not put credentials in shell history).
Use two lines:
- COSMOS username
- COSMOS password
Configuration
Create and edit the user config file:
euclidkit init-config --output ~/.euclidkit/euclidkit_config.yaml --template basic
Then edit ~/.euclidkit/euclidkit_config.yaml and set the credential path.
Set the credential path in the config:
data:
credentials_file: /home/<user>/.euclidkit/.cred.txt
Basic Usage
# Note: the Python import path is currently still `euclidkit`.
from euclidkit.core.data_access import EuclidArchive
# Initialize archive connection
archive = EuclidArchive(environment='PDR')
archive.login()
# Crossmatch your sources with Euclid MER catalogue
results = archive.crossmatch_sources(
user_table="my_sources.csv",
radius=1.0, # arcseconds
output_file="crossmatch_results.fits"
)
# Query for available spectra
spectra_table = archive.query_spectra_sources(
crossmatch_table=results,
output_file="spectra_sources.fits"
)
# Export local Datalabs spectra rows to Parquet parts
from euclidkit.core.spectra_parquet import spectra_to_parquet
stats = spectra_to_parquet(
catalog_table="spectra_sources.fits",
output_prefix="./output/raw_spectra",
lambda_range="RGS",
)
print(stats.output_files)
Command Line Interface
Crossmatching Sources
# Crossmatch user table with Euclid MER catalogue
euclidkit crossmatch \
--input my_sources.csv \
--output crossmatch_results.fits \
--radius 1.0 \
--verbose
# Optionally remove columns that are entirely null/missing from the final
# output table. Zero, False, and empty-string columns are retained.
euclidkit crossmatch \
--input my_sources.csv \
--output crossmatch_results.fits \
--drop-empty-columns
# Submit the entire table as a single async job (no batching). The output file
# uses async TAP mode; for very large tables euclidkit splits into async chunks.
euclidkit crossmatch \
--input my_sources.csv \
--output crossmatch_results.fits \
--full-async \
--async-chunk-size 500000
# When using the IDR environment the command defaults to the WIDE field and
# writes results to wide_<filename>. Use --idr-field DEEP to query DEEP MER.
# DEEP defaults to the deep_survey partition (EDFN, EDFF, EDFS).
euclidkit crossmatch \
--input my_sources.csv \
--output crossmatch_results.fits \
--environment IDR \
--idr-field DEEP
# Query the deep_mode partition (CDFS, COSMOS), or use "both" for
# deep_survey followed by deep_mode.
euclidkit crossmatch \
--input my_sources.csv \
--output crossmatch_results.fits \
--environment IDR \
--idr-field DEEP \
--idr-deep-partition mode
# Crossmatch an already-uploaded archive user table (no local upload needed)
euclidkit crossmatch \
--user-table-name my_table \
--output crossmatch_results.fits \
--match-mode object-id \
--environment IDR \
--idr-field WIDE
--full-async behavior:
- For smaller inputs, euclidkit submits one async TAP job, downloads the result to the requested output file, and then removes the remote job.
- For large local input tables (
--input), euclidkit splits the upload into async chunks, saves each chunk to<output>_part_####.fits, removes each remote job after the chunk is saved, writes<output>.manifest.json, and merges the chunk files into the requested final output. - For large archive user tables (
--user-table-name), euclidkit uses the same on-disk chunking pattern and final merge.
Matching mode recommendation:
- Prefer
--match-mode object-idwhenever the input already contains Euclidobject_idvalues, orsource_idvalues that should be joined to MERobject_id. This avoids positional matching and is usually faster and more robust for large tables.
--max-sources vs --async-chunk-size:
--max-sources: limits how many rows from the input table are processed in total.--async-chunk-size: controls rows per async TAP job when--full-asyncis enabled.--drop-empty-columns: drops columns where every result value is null or missing before saving the final--outputtable. Intermediate async part files are left unchanged.
Uploading Tables
# Upload a FITS table to your Euclid TAP workspace
euclidkit upload-table \
--input my_sources.fits \
--table-name my_workspace_table \
--description "Sources awaiting deep crossmatch" \
--overwrite
# Upload CSV data as-is (format inferred automatically)
euclidkit upload-table \
--input trimmed_sources.csv \
--table-name trimmed_sources
Querying Spectra
# Query spectra-source rows from an ID or coordinate table.
# Auto mode uses object_id when present, otherwise spatial RA/Dec matching.
euclidkit query-spectra \
--crossmatch my_spectral_ids_or_coordinates.fits \
--output spectra_sources.fits \
--environment IDR \
--idr-field WIDE \
--verbose
query-spectra supports --match-mode auto|object-id|spatial. Object-ID mode
joins spectra_source.source_id = object_id; spatial mode matches input
coordinates to ra_obj/dec_obj with nearest-only output. Spatial mode accepts
common RA/Dec aliases such as RA/DEC, right_ascension/declination, and
ra_deg/dec_deg case-insensitively. A MER crossmatch table is optional; any
local table with the needed spectra-source IDs or coordinates can be used. The
result is the usual input to compile-spectra; see the Sphinx spectra
compilation guide for the full Datalabs and Datalink workflow details.
Querying Segmentation Maps
# Resolve segmentation-map files and WCS metadata from MER crossmatch results.
# The input must contain SEGMENTATION_MAP_ID, object_id, and coordinates.
# query-segmap prefers ra/dec and falls back to mer_ra/mer_dec.
euclidkit query-segmap \
--input crossmatch_results.fits \
--output segmentation_maps.fits \
--environment IDR
# Create one raw-label 10 arcsec FITS cutout per source row
euclidkit compile-segmap \
--input segmentation_maps.fits \
--output-dir ./segmap_cutouts
query-segmap computes tile_index = floor(SEGMENTATION_MAP_ID / 1_000_000)
locally, then joins to q1.mer_segmentation_map for PDR,
dr1.mer_segmentation_map for IDR, and sedm.mer_segmentation_map for OTF/REG.
Run euclidkit crossmatch first if your table does not yet contain
SEGMENTATION_MAP_ID. compile-segmap reads local segmentation-map FITS files
from datalabs_path + file_name, groups rows by tile file, and preserves the
raw segmentation-label pixels in each cutout. See the segmentation-map guide in
the Sphinx docs for required columns, output filenames, and error-handling
options.
Building Cutana Input
# Build Cutana CSV from a source table with object_id or ra/dec columns
euclidkit query-cutana \
--sources my_sources.fits \
--output cutana_input.csv \
--instrument VIS \
--cutout-size arcsec \
--cutout-size-value 15
# NISP example with explicit filters
euclidkit query-cutana \
--sources my_sources.fits \
--output cutana_input_nisp.csv \
--instrument NISP \
--nisp-filters NIR_Y,NIR_H \
--environment IDR \
--idr-field DEEP \
--idr-deep-partition both \
--cutout-size arcsec \
--cutout-size-value 15
Compiling Spectra
# Default local Datalabs mode: export raw spectra to parquet parts
euclidkit compile-spectra \
--spectra-table spectra_sources.fits \
--output-dir ./output \
--prefix raw_spectra \
--chunk-size 2000 \
--workers 8 \
-L RGS
# Export both arms into separate raw_spectra_rgs / raw_spectra_bgs parquet families
euclidkit compile-spectra \
--spectra-table spectra_sources.fits \
--output-dir ./output \
--prefix raw_spectra \
-L BOTH
# Legacy local FITS mode remains available explicitly
euclidkit compile-spectra \
--spectra-table spectra_sources.fits \
--output-dir ./output \
--prefix compiled_spectra \
--output-format fits \
--max-extensions 1000
# Datalink mode is unchanged and writes FITS outputs
euclidkit compile-spectra \
--spectra-table spectra_sources.fits \
--output-dir ./output \
--prefix compiled_dl \
--use-datalink \
--environment IDR \
--schema sedm \
-L BOTH
# Export per-dither spectra from local Datalabs FITS files
euclidkit dithers-to-parquet \
--catalog-table spectra_sources.fits \
--output-prefix ./output/raw_sir \
--workers 8 \
--lambda-range RGS \
--environment IDR
Note: non-Datalink compile-spectra now defaults to Parquet and reads LRANGE from FITS headers for -L/--lambda-range filtering. Datalink remains FITS-only; RGS/BGS map to corresponding retrieval types, and BOTH runs two passes and writes separate _rgs and _bgs FITS files. --retrieval-type is kept for backward compatibility.
dithers-to-parquet automatically annotates per-dither rows with
obs_time_mjd, obs_time_utc, and pa from the archive raw-frame table.
PDR/Q1 uses q1.raw_frame; IDR/DR1 uses dr1.raw_frame; OTF/REG use
sedm.raw_frame. It matches raw_frame.pointing_id to the dither HDU ptgid
and requires raw_frame.grism_wheel_pos = gwa_pos, falling back to parsed
dither_id only when ptgid is unavailable. Internally, it uses a TAP upload
join rather than a long literal ID-list clause:
SELECT
r.pointing_id,
r.grism_wheel_pos,
r.obs_time_mjd,
r.obs_time_utc,
r.pa
FROM dr1.raw_frame AS r
JOIN TAP_UPLOAD.dither_pointings AS p
ON r.pointing_id = p.pointing_id
AND r.grism_wheel_pos = p.gwa_pos
Key Features
Data Archive Integration
- Multiple Environments: Support for PDR, IDR, OTF, and REG archive environments
- Efficient Queries: Batch processing with TAP table uploads for large datasets
- Crossmatching: Position-based matching with configurable search radius
Spectroscopic Tools
- Spectrum Access: Direct access to Euclid data volumes on ESA Datalabs
- Parquet Spectra Export: Export local Datalabs spectra to raw Parquet parts by default
- FITS Compatibility: Keep legacy multi-extension FITS compilation and Datalink FITS outputs available
Analysis Pipeline
- Quality Control: Spectrum validation and quality assessment
Data Environment
ESA Datalabs Integration
This package is optimized for the ESA Datalabs environment with direct access to:
- Euclid Q1 Data:
/data/euclid_q1/(35 TB volume)
API Reference
Core Classes
EuclidArchive
Main interface to the Euclid science archive.
archive = EuclidArchive(environment='PDR')
archive.login(credentials_file='~/.euclidkit/.cred.txt')
# Crossmatch sources
results = archive.crossmatch_sources(
user_table="sources.csv",
radius=1.0,
output_file="results.fits"
)
# Query spectra
spectra = archive.query_spectra_sources(
crossmatch_table=results,
output_file="spectra.fits"
)
# Get individual spectrum
spectrum_hdu = archive.get_individual_spectrum(
datalabs_path="/data/euclid_q1/path",
file_name="spectrum_file.fits",
hdu_index=42
)
# Export queried local Datalabs spectra to raw Parquet parts
from euclidkit.core.spectra_parquet import spectra_to_parquet
stats = spectra_to_parquet(
catalog_table="spectra.fits",
output_prefix="./output/raw_spectra",
chunk_size=2000,
lambda_range="RGS",
)
print(stats.manifest_path)
Spectra Parquet Export
Default local Datalabs spectra export writes raw Parquet parts directly from
catalog rows containing datalabs_path, file_name, hdu_index, source_id,
object_id, ra_obj, and dec_obj.
from euclidkit.core.spectra_parquet import spectra_to_parquet, dithers_to_parquet
raw_stats = spectra_to_parquet(
catalog_table="spectra_sources.fits",
output_prefix="./output/raw_spectra",
chunk_size=2000,
workers=8,
lambda_range="RGS",
on_error="skip",
)
dither_stats = dithers_to_parquet(
catalog_table="spectra_sources.fits",
output_prefix="./output/raw_sir",
chunk_size=2000,
workers=8,
lambda_range="RGS",
include_combined=True,
environment="IDR",
)
SpectrumCompiler remains available for legacy local multi-extension FITS
compilation and Datalink FITS outputs.
Workflow Examples
Complete Spectroscopic Analysis Pipeline
from euclidkit.core.data_access import EuclidArchive
from euclidkit.core.spectra_parquet import spectra_to_parquet, dithers_to_parquet
import pandas as pd
# 1. Initialize archive
archive = EuclidArchive(environment='PDR')
archive.login()
# 2. Load your QSO candidates
qso_candidates = pd.read_csv('qso_candidates.csv')
# 3. Crossmatch with Euclid MER catalogue
crossmatches = archive.crossmatch_sources(
user_table=qso_candidates,
radius=2.0, # 2 arcsecond radius
output_file='qso_crossmatches.fits'
)
# 4. Find available spectra
spectra_sources = archive.query_spectra_sources(
crossmatch_table=crossmatches,
output_file='qso_spectra_sources.fits'
)
print(f"Found {len(spectra_sources)} spectra for {len(crossmatches)} crossmatches")
# 5. Export raw spectra to Parquet parts for downstream analysis
raw_stats = spectra_to_parquet(
catalog_table='qso_spectra_sources.fits',
output_prefix='./spectra_parquet/qso_raw',
chunk_size=2000,
lambda_range='RGS',
on_error='skip',
)
print(f"Wrote {len(raw_stats.output_files)} raw spectra parquet parts")
# 6. Optionally export per-dither spectra for the same catalog rows
dither_stats = dithers_to_parquet(
catalog_table='qso_spectra_sources.fits',
output_prefix='./spectra_parquet/qso_sir',
lambda_range='RGS',
environment='IDR',
)
print(f"Wrote {len(dither_stats.dither_output_files)} dither parquet parts")
archive.logout()
Diagnostics
Check your installation and environment:
# Check all components
euclidkit diagnostics
# Check specific components
euclidkit diagnostics --check-deps --check-data
Archive Environments
Use --environment (CLI) or environment=... (Python API) to select the
archive backend:
- PDR: Public Data Release archive.
- IDR: Internal Data Release archive (consortium access).
- OTF: On-the-fly archive environment.
- REG: Regression/testing archive environment.
For IDR, you can also select the field with --idr-field:
- WIDE: Uses the IDR WIDE MER catalogue. This queries
catalogue.mer_catalogue_wide_surveyfirst, then queriescatalogue.mer_catalogue_wide_modeonly for sources not matched in the survey table. - DEEP: Uses IDR DEEP MER partitions. By default this queries
catalogue.mer_catalogue_deep_survey(EDFN, EDFF, EDFS). Use--idr-deep-partition modeforcatalogue.mer_catalogue_deep_mode(CDFS, COSMOS), or--idr-deep-partition bothto query survey first and mode second.
Examples:
# IDR WIDE (default IDR field)
euclidkit crossmatch \
--input my_sources.fits \
--output xmatch_wide.fits \
--environment IDR \
--idr-field WIDE
# IDR DEEP
euclidkit crossmatch \
--input my_sources.fits \
--output xmatch_deep.fits \
--environment IDR \
--idr-field DEEP
# IDR DEEP mode partition
euclidkit crossmatch \
--input my_sources.fits \
--output xmatch_deep_mode.fits \
--environment IDR \
--idr-field DEEP \
--idr-deep-partition mode
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Documentation
For detailed documentation and examples, visit:
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: fuympku@outlook.com
Author
Yuming Fu (@rudolffu)
- Email: fuympku@outlook.com
- GitHub: https://github.com/rudolffu/euclidkit
License
This project is licensed under the BSD 3-Clause License - see the LICENSE file for details.
Acknowledgments
- ESA Euclid Mission and Euclid Consortium
- ESA Datalabs and Euclid Data Space infrastructure team
- Astropy and astroquery communities
Changelog
Latest Changes
- Spectroscopic Pipeline: Complete pipeline for accessing and combining Euclid spectra
- CLI Integration: Added
--combine-outputoption toquery-spectracommand - TAP Upload: Improved query performance using TAP table uploads
- Parquet Spectra Export: Default local Datalabs spectra export to Parquet
- Error Handling: Robust handling of long filenames and missing data
See CHANGELOG.md for detailed version history.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file euclidkit-0.2.4.tar.gz.
File metadata
- Download URL: euclidkit-0.2.4.tar.gz
- Upload date:
- Size: 2.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65d19ad54c07be8eccf97e8fb68eb4eef717dab9f08a9e7d8e0c735899b43141
|
|
| MD5 |
736dff3b164e24803fcddc44bf1bc860
|
|
| BLAKE2b-256 |
b52334b0d06dd0d00c55059946aadbb20e3e449ca2f72b716e3ff6bc764f05ef
|
Provenance
The following attestation bundles were made for euclidkit-0.2.4.tar.gz:
Publisher:
python-publish.yml on rudolffu/euclidkit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
euclidkit-0.2.4.tar.gz -
Subject digest:
65d19ad54c07be8eccf97e8fb68eb4eef717dab9f08a9e7d8e0c735899b43141 - Sigstore transparency entry: 2101347999
- Sigstore integration time:
-
Permalink:
rudolffu/euclidkit@01b5f9808b7f5ca5f56bca4946f7e868d6cbe7d0 -
Branch / Tag:
refs/tags/v0.2.4 - Owner: https://github.com/rudolffu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@01b5f9808b7f5ca5f56bca4946f7e868d6cbe7d0 -
Trigger Event:
release
-
Statement type:
File details
Details for the file euclidkit-0.2.4-py3-none-any.whl.
File metadata
- Download URL: euclidkit-0.2.4-py3-none-any.whl
- Upload date:
- Size: 2.1 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59795444b0371e99ffd314fa5ff1748271023e18993436477b685bd69d7f7586
|
|
| MD5 |
8fba67ffdffedb1dd08fe1305750e67d
|
|
| BLAKE2b-256 |
062bc2c3cca5ef3a383b4487b4dd4aa032364dd67f8a6cdaa62cf6f38d14c850
|
Provenance
The following attestation bundles were made for euclidkit-0.2.4-py3-none-any.whl:
Publisher:
python-publish.yml on rudolffu/euclidkit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
euclidkit-0.2.4-py3-none-any.whl -
Subject digest:
59795444b0371e99ffd314fa5ff1748271023e18993436477b685bd69d7f7586 - Sigstore transparency entry: 2101349024
- Sigstore integration time:
-
Permalink:
rudolffu/euclidkit@01b5f9808b7f5ca5f56bca4946f7e868d6cbe7d0 -
Branch / Tag:
refs/tags/v0.2.4 - Owner: https://github.com/rudolffu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@01b5f9808b7f5ca5f56bca4946f7e868d6cbe7d0 -
Trigger Event:
release
-
Statement type: