mirror of
https://github.com/K-Dense-AI/claude-scientific-skills.git
synced 2026-03-28 07:33:45 +08:00
Add more scientific skills
This commit is contained in:
790
scientific-packages/astropy/SKILL.md
Normal file
790
scientific-packages/astropy/SKILL.md
Normal file
@@ -0,0 +1,790 @@
|
||||
---
|
||||
name: astropy
|
||||
description: Comprehensive toolkit for astronomical data analysis and computation using the astropy Python library. This skill should be used when working with astronomical data including FITS files, coordinate transformations, cosmological calculations, time systems, physical units, data tables, model fitting, WCS transformations, and visualization. Use this skill for tasks involving celestial coordinates, astronomical file formats, photometry, spectroscopy, or any astronomy-specific Python computations.
|
||||
---
|
||||
|
||||
# Astropy
|
||||
|
||||
## Overview
|
||||
|
||||
Astropy is the community standard Python library for astronomy, providing core functionality for astronomical data analysis and computation. This skill provides comprehensive guidance and tools for working with astropy's extensive capabilities across coordinate systems, file I/O, units and quantities, time systems, cosmology, modeling, and more.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Use this skill when:
|
||||
- Working with FITS files (reading, writing, inspecting, modifying)
|
||||
- Performing coordinate transformations between astronomical reference frames
|
||||
- Calculating cosmological distances, ages, or other quantities
|
||||
- Handling astronomical time systems and conversions
|
||||
- Working with physical units and dimensional analysis
|
||||
- Processing astronomical data tables with specialized column types
|
||||
- Fitting models to astronomical data
|
||||
- Converting between pixel and world coordinates (WCS)
|
||||
- Performing robust statistical analysis on astronomical data
|
||||
- Visualizing astronomical images with proper scaling and stretching
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
### 1. FITS File Operations
|
||||
|
||||
FITS (Flexible Image Transport System) is the standard file format in astronomy. Astropy provides comprehensive FITS support.
|
||||
|
||||
**Quick FITS Inspection**:
|
||||
Use the included `scripts/fits_info.py` script for rapid file inspection:
|
||||
```bash
|
||||
python scripts/fits_info.py observation.fits
|
||||
python scripts/fits_info.py observation.fits --detailed
|
||||
python scripts/fits_info.py observation.fits --ext 1
|
||||
```
|
||||
|
||||
**Common FITS workflows**:
|
||||
```python
|
||||
from astropy.io import fits
|
||||
|
||||
# Read FITS file
|
||||
with fits.open('image.fits') as hdul:
|
||||
hdul.info() # Display structure
|
||||
data = hdul[0].data
|
||||
header = hdul[0].header
|
||||
|
||||
# Write FITS file
|
||||
fits.writeto('output.fits', data, header, overwrite=True)
|
||||
|
||||
# Quick access (less efficient for multiple operations)
|
||||
data = fits.getdata('image.fits', ext=0)
|
||||
header = fits.getheader('image.fits', ext=0)
|
||||
|
||||
# Update specific header keyword
|
||||
fits.setval('image.fits', 'OBJECT', value='M31')
|
||||
```
|
||||
|
||||
**Multi-extension FITS**:
|
||||
```python
|
||||
from astropy.io import fits
|
||||
|
||||
# Create multi-extension FITS
|
||||
primary = fits.PrimaryHDU(primary_data)
|
||||
image_ext = fits.ImageHDU(science_data, name='SCI')
|
||||
error_ext = fits.ImageHDU(error_data, name='ERR')
|
||||
|
||||
hdul = fits.HDUList([primary, image_ext, error_ext])
|
||||
hdul.writeto('multi_ext.fits', overwrite=True)
|
||||
```
|
||||
|
||||
**Binary tables**:
|
||||
```python
|
||||
from astropy.io import fits
|
||||
|
||||
# Read binary table
|
||||
with fits.open('catalog.fits') as hdul:
|
||||
table_data = hdul[1].data
|
||||
ra = table_data['RA']
|
||||
dec = table_data['DEC']
|
||||
|
||||
# Better: use astropy.table for table operations (see section 5)
|
||||
```
|
||||
|
||||
### 2. Coordinate Systems and Transformations
|
||||
|
||||
Astropy supports ~25 coordinate frames with seamless transformations.
|
||||
|
||||
**Quick Coordinate Conversion**:
|
||||
Use the included `scripts/coord_convert.py` script:
|
||||
```bash
|
||||
python scripts/coord_convert.py 10.68 41.27 --from icrs --to galactic
|
||||
python scripts/coord_convert.py --file coords.txt --from icrs --to galactic --output sexagesimal
|
||||
```
|
||||
|
||||
**Basic coordinate operations**:
|
||||
```python
|
||||
from astropy.coordinates import SkyCoord
|
||||
import astropy.units as u
|
||||
|
||||
# Create coordinate (multiple input formats supported)
|
||||
c = SkyCoord(ra=10.68*u.degree, dec=41.27*u.degree, frame='icrs')
|
||||
c = SkyCoord('00:42:44.3 +41:16:09', unit=(u.hourangle, u.deg))
|
||||
c = SkyCoord('00h42m44.3s +41d16m09s')
|
||||
|
||||
# Transform between frames
|
||||
c_galactic = c.galactic
|
||||
c_fk5 = c.fk5
|
||||
|
||||
print(f"Galactic: l={c_galactic.l.deg:.3f}, b={c_galactic.b.deg:.3f}")
|
||||
```
|
||||
|
||||
**Working with coordinate arrays**:
|
||||
```python
|
||||
import numpy as np
|
||||
from astropy.coordinates import SkyCoord
|
||||
import astropy.units as u
|
||||
|
||||
# Arrays of coordinates
|
||||
ra = np.array([10.1, 10.2, 10.3]) * u.degree
|
||||
dec = np.array([40.1, 40.2, 40.3]) * u.degree
|
||||
coords = SkyCoord(ra=ra, dec=dec, frame='icrs')
|
||||
|
||||
# Calculate separations
|
||||
sep = coords[0].separation(coords[1])
|
||||
print(f"Separation: {sep.to(u.arcmin)}")
|
||||
|
||||
# Position angle
|
||||
pa = coords[0].position_angle(coords[1])
|
||||
```
|
||||
|
||||
**Catalog matching**:
|
||||
```python
|
||||
from astropy.coordinates import SkyCoord
|
||||
import astropy.units as u
|
||||
|
||||
catalog1 = SkyCoord(ra=[10, 11, 12]*u.degree, dec=[40, 41, 42]*u.degree)
|
||||
catalog2 = SkyCoord(ra=[10.01, 11.02, 13]*u.degree, dec=[40.01, 41.01, 43]*u.degree)
|
||||
|
||||
# Find nearest neighbors
|
||||
idx, sep2d, dist3d = catalog1.match_to_catalog_sky(catalog2)
|
||||
|
||||
# Filter by separation threshold
|
||||
max_sep = 1 * u.arcsec
|
||||
matched = sep2d < max_sep
|
||||
```
|
||||
|
||||
**Horizontal coordinates (Alt/Az)**:
|
||||
```python
|
||||
from astropy.coordinates import SkyCoord, EarthLocation, AltAz
|
||||
from astropy.time import Time
|
||||
import astropy.units as u
|
||||
|
||||
location = EarthLocation(lat=40*u.deg, lon=-70*u.deg, height=300*u.m)
|
||||
obstime = Time('2023-01-01 03:00:00')
|
||||
target = SkyCoord(ra=10*u.degree, dec=40*u.degree, frame='icrs')
|
||||
|
||||
altaz_frame = AltAz(obstime=obstime, location=location)
|
||||
target_altaz = target.transform_to(altaz_frame)
|
||||
|
||||
print(f"Alt: {target_altaz.alt.deg:.2f}°, Az: {target_altaz.az.deg:.2f}°")
|
||||
```
|
||||
|
||||
**Available coordinate frames**:
|
||||
- `icrs` - International Celestial Reference System (default, preferred)
|
||||
- `fk5`, `fk4` - Fifth/Fourth Fundamental Katalog
|
||||
- `galactic` - Galactic coordinates
|
||||
- `supergalactic` - Supergalactic coordinates
|
||||
- `altaz` - Horizontal (altitude-azimuth) coordinates
|
||||
- `gcrs`, `cirs`, `itrs` - Earth-based systems
|
||||
- Ecliptic frames: `BarycentricMeanEcliptic`, `HeliocentricMeanEcliptic`, `GeocentricMeanEcliptic`
|
||||
|
||||
### 3. Units and Quantities
|
||||
|
||||
Physical units are fundamental to astronomical calculations. Astropy's units system provides dimensional analysis and automatic conversions.
|
||||
|
||||
**Basic unit operations**:
|
||||
```python
|
||||
import astropy.units as u
|
||||
|
||||
# Create quantities
|
||||
distance = 5.2 * u.parsec
|
||||
velocity = 300 * u.km / u.s
|
||||
time = 10 * u.year
|
||||
|
||||
# Convert units
|
||||
distance_ly = distance.to(u.lightyear)
|
||||
velocity_mps = velocity.to(u.m / u.s)
|
||||
|
||||
# Arithmetic with units
|
||||
wavelength = 500 * u.nm
|
||||
frequency = wavelength.to(u.Hz, equivalencies=u.spectral())
|
||||
```
|
||||
|
||||
**Working with arrays**:
|
||||
```python
|
||||
import numpy as np
|
||||
import astropy.units as u
|
||||
|
||||
wavelengths = np.array([400, 500, 600]) * u.nm
|
||||
frequencies = wavelengths.to(u.THz, equivalencies=u.spectral())
|
||||
|
||||
fluxes = np.array([1.2, 2.3, 1.8]) * u.Jy
|
||||
luminosities = 4 * np.pi * (10*u.pc)**2 * fluxes
|
||||
```
|
||||
|
||||
**Important equivalencies**:
|
||||
- `u.spectral()` - Convert wavelength ↔ frequency ↔ energy
|
||||
- `u.doppler_optical(rest)` - Optical Doppler velocity
|
||||
- `u.doppler_radio(rest)` - Radio Doppler velocity
|
||||
- `u.doppler_relativistic(rest)` - Relativistic Doppler
|
||||
- `u.temperature()` - Temperature unit conversions
|
||||
- `u.brightness_temperature(freq)` - Brightness temperature
|
||||
|
||||
**Physical constants**:
|
||||
```python
|
||||
from astropy import constants as const
|
||||
|
||||
print(const.c) # Speed of light
|
||||
print(const.G) # Gravitational constant
|
||||
print(const.M_sun) # Solar mass
|
||||
print(const.R_sun) # Solar radius
|
||||
print(const.L_sun) # Solar luminosity
|
||||
```
|
||||
|
||||
**Performance tip**: Use the `<<` operator for fast unit assignment to arrays:
|
||||
```python
|
||||
# Fast
|
||||
result = large_array << u.m
|
||||
|
||||
# Slower
|
||||
result = large_array * u.m
|
||||
```
|
||||
|
||||
### 4. Time Systems
|
||||
|
||||
Astronomical time systems require high precision and multiple time scales.
|
||||
|
||||
**Creating time objects**:
|
||||
```python
|
||||
from astropy.time import Time
|
||||
import astropy.units as u
|
||||
|
||||
# Various input formats
|
||||
t1 = Time('2023-01-01T00:00:00', format='isot', scale='utc')
|
||||
t2 = Time(2459945.5, format='jd', scale='utc')
|
||||
t3 = Time(['2023-01-01', '2023-06-01'], format='iso')
|
||||
|
||||
# Convert formats
|
||||
print(t1.jd) # Julian Date
|
||||
print(t1.mjd) # Modified Julian Date
|
||||
print(t1.unix) # Unix timestamp
|
||||
print(t1.iso) # ISO format
|
||||
|
||||
# Convert time scales
|
||||
print(t1.tai) # International Atomic Time
|
||||
print(t1.tt) # Terrestrial Time
|
||||
print(t1.tdb) # Barycentric Dynamical Time
|
||||
```
|
||||
|
||||
**Time arithmetic**:
|
||||
```python
|
||||
from astropy.time import Time, TimeDelta
|
||||
import astropy.units as u
|
||||
|
||||
t1 = Time('2023-01-01T00:00:00')
|
||||
dt = TimeDelta(1*u.day)
|
||||
|
||||
t2 = t1 + dt
|
||||
diff = t2 - t1
|
||||
print(diff.to(u.hour))
|
||||
|
||||
# Array of times
|
||||
times = t1 + np.arange(10) * u.day
|
||||
```
|
||||
|
||||
**Astronomical time calculations**:
|
||||
```python
|
||||
from astropy.time import Time
|
||||
from astropy.coordinates import SkyCoord, EarthLocation
|
||||
import astropy.units as u
|
||||
|
||||
location = EarthLocation(lat=40*u.deg, lon=-70*u.deg)
|
||||
t = Time('2023-01-01T00:00:00')
|
||||
|
||||
# Local sidereal time
|
||||
lst = t.sidereal_time('apparent', longitude=location.lon)
|
||||
|
||||
# Barycentric correction
|
||||
target = SkyCoord(ra=10*u.deg, dec=40*u.deg)
|
||||
ltt = t.light_travel_time(target, location=location)
|
||||
t_bary = t.tdb + ltt
|
||||
```
|
||||
|
||||
**Available time scales**:
|
||||
- `utc` - Coordinated Universal Time
|
||||
- `tai` - International Atomic Time
|
||||
- `tt` - Terrestrial Time
|
||||
- `tcb`, `tcg` - Barycentric/Geocentric Coordinate Time
|
||||
- `tdb` - Barycentric Dynamical Time
|
||||
- `ut1` - Universal Time
|
||||
|
||||
### 5. Data Tables
|
||||
|
||||
Astropy tables provide astronomy-specific enhancements over pandas.
|
||||
|
||||
**Creating and manipulating tables**:
|
||||
```python
|
||||
from astropy.table import Table
|
||||
import astropy.units as u
|
||||
|
||||
# Create table
|
||||
t = Table()
|
||||
t['name'] = ['Star1', 'Star2', 'Star3']
|
||||
t['ra'] = [10.5, 11.2, 12.3] * u.degree
|
||||
t['dec'] = [41.2, 42.1, 43.5] * u.degree
|
||||
t['flux'] = [1.2, 2.3, 0.8] * u.Jy
|
||||
|
||||
# Column metadata
|
||||
t['flux'].description = 'Flux at 1.4 GHz'
|
||||
t['flux'].format = '.2f'
|
||||
|
||||
# Add calculated column
|
||||
t['flux_mJy'] = t['flux'].to(u.mJy)
|
||||
|
||||
# Filter and sort
|
||||
bright = t[t['flux'] > 1.0 * u.Jy]
|
||||
t.sort('flux')
|
||||
```
|
||||
|
||||
**Table I/O**:
|
||||
```python
|
||||
from astropy.table import Table
|
||||
|
||||
# Read (format auto-detected from extension)
|
||||
t = Table.read('data.fits')
|
||||
t = Table.read('data.csv', format='ascii.csv')
|
||||
t = Table.read('data.ecsv', format='ascii.ecsv') # Preserves units!
|
||||
t = Table.read('data.votable', format='votable')
|
||||
|
||||
# Write
|
||||
t.write('output.fits', overwrite=True)
|
||||
t.write('output.ecsv', format='ascii.ecsv', overwrite=True)
|
||||
```
|
||||
|
||||
**Advanced operations**:
|
||||
```python
|
||||
from astropy.table import Table, join, vstack, hstack
|
||||
|
||||
# Join tables (like SQL)
|
||||
joined = join(table1, table2, keys='id')
|
||||
|
||||
# Stack tables
|
||||
combined_rows = vstack([t1, t2])
|
||||
combined_cols = hstack([t1, t2])
|
||||
|
||||
# Grouping and aggregation
|
||||
t.group_by('category').groups.aggregate(np.mean)
|
||||
```
|
||||
|
||||
**Tables with astronomical objects**:
|
||||
```python
|
||||
from astropy.table import Table
|
||||
from astropy.coordinates import SkyCoord
|
||||
from astropy.time import Time
|
||||
import astropy.units as u
|
||||
|
||||
coords = SkyCoord(ra=[10, 11, 12]*u.deg, dec=[40, 41, 42]*u.deg)
|
||||
times = Time(['2023-01-01', '2023-01-02', '2023-01-03'])
|
||||
|
||||
t = Table([coords, times], names=['coords', 'obstime'])
|
||||
print(t['coords'][0].ra) # Access coordinate properties
|
||||
```
|
||||
|
||||
### 6. Cosmological Calculations
|
||||
|
||||
Quick cosmology calculations using standard models.
|
||||
|
||||
**Using the cosmology calculator**:
|
||||
```bash
|
||||
python scripts/cosmo_calc.py 0.5 1.0 1.5
|
||||
python scripts/cosmo_calc.py --range 0 3 0.5 --cosmology Planck18
|
||||
python scripts/cosmo_calc.py 0.5 --verbose
|
||||
python scripts/cosmo_calc.py --convert 1000 --from luminosity_distance
|
||||
```
|
||||
|
||||
**Programmatic usage**:
|
||||
```python
|
||||
from astropy.cosmology import Planck18
|
||||
import astropy.units as u
|
||||
import numpy as np
|
||||
|
||||
cosmo = Planck18
|
||||
|
||||
# Calculate distances
|
||||
z = 1.5
|
||||
d_L = cosmo.luminosity_distance(z)
|
||||
d_A = cosmo.angular_diameter_distance(z)
|
||||
d_C = cosmo.comoving_distance(z)
|
||||
|
||||
# Time calculations
|
||||
age = cosmo.age(z)
|
||||
lookback = cosmo.lookback_time(z)
|
||||
|
||||
# Hubble parameter
|
||||
H_z = cosmo.H(z)
|
||||
|
||||
print(f"At z={z}:")
|
||||
print(f" Luminosity distance: {d_L:.2f}")
|
||||
print(f" Age of universe: {age:.2f}")
|
||||
```
|
||||
|
||||
**Convert observables**:
|
||||
```python
|
||||
from astropy.cosmology import Planck18
|
||||
import astropy.units as u
|
||||
|
||||
cosmo = Planck18
|
||||
z = 1.5
|
||||
|
||||
# Angular size to physical size
|
||||
d_A = cosmo.angular_diameter_distance(z)
|
||||
angular_size = 1 * u.arcsec
|
||||
physical_size = (angular_size.to(u.radian) * d_A).to(u.kpc)
|
||||
|
||||
# Flux to luminosity
|
||||
flux = 1e-17 * u.erg / u.s / u.cm**2
|
||||
d_L = cosmo.luminosity_distance(z)
|
||||
luminosity = flux * 4 * np.pi * d_L**2
|
||||
|
||||
# Find redshift for given distance
|
||||
from astropy.cosmology import z_at_value
|
||||
z = z_at_value(cosmo.luminosity_distance, 1000*u.Mpc)
|
||||
```
|
||||
|
||||
**Available cosmologies**:
|
||||
- `Planck18`, `Planck15`, `Planck13` - Planck satellite parameters
|
||||
- `WMAP9`, `WMAP7`, `WMAP5` - WMAP satellite parameters
|
||||
- Custom: `FlatLambdaCDM(H0=70*u.km/u.s/u.Mpc, Om0=0.3)`
|
||||
|
||||
### 7. Model Fitting
|
||||
|
||||
Fit mathematical models to astronomical data.
|
||||
|
||||
**1D fitting example**:
|
||||
```python
|
||||
from astropy.modeling import models, fitting
|
||||
import numpy as np
|
||||
|
||||
# Generate data
|
||||
x = np.linspace(0, 10, 100)
|
||||
y_data = 10 * np.exp(-0.5 * ((x - 5) / 1)**2) + np.random.normal(0, 0.5, x.shape)
|
||||
|
||||
# Create and fit model
|
||||
g_init = models.Gaussian1D(amplitude=8, mean=4.5, stddev=0.8)
|
||||
fitter = fitting.LevMarLSQFitter()
|
||||
g_fit = fitter(g_init, x, y_data)
|
||||
|
||||
# Results
|
||||
print(f"Amplitude: {g_fit.amplitude.value:.3f}")
|
||||
print(f"Mean: {g_fit.mean.value:.3f}")
|
||||
print(f"Stddev: {g_fit.stddev.value:.3f}")
|
||||
|
||||
# Evaluate fitted model
|
||||
y_fit = g_fit(x)
|
||||
```
|
||||
|
||||
**Common 1D models**:
|
||||
- `Gaussian1D` - Gaussian profile
|
||||
- `Lorentz1D` - Lorentzian profile
|
||||
- `Voigt1D` - Voigt profile
|
||||
- `Moffat1D` - Moffat profile (PSF modeling)
|
||||
- `Polynomial1D` - Polynomial
|
||||
- `PowerLaw1D` - Power law
|
||||
- `BlackBody` - Blackbody spectrum
|
||||
|
||||
**Common 2D models**:
|
||||
- `Gaussian2D` - 2D Gaussian
|
||||
- `Moffat2D` - 2D Moffat (stellar PSF)
|
||||
- `AiryDisk2D` - Airy disk (diffraction pattern)
|
||||
- `Disk2D` - Circular disk
|
||||
|
||||
**Fitting with constraints**:
|
||||
```python
|
||||
from astropy.modeling import models, fitting
|
||||
|
||||
g = models.Gaussian1D(amplitude=10, mean=5, stddev=1)
|
||||
|
||||
# Set bounds
|
||||
g.amplitude.bounds = (0, None) # Positive only
|
||||
g.mean.bounds = (4, 6) # Constrain center
|
||||
|
||||
# Fix parameters
|
||||
g.stddev.fixed = True
|
||||
|
||||
# Compound models
|
||||
model = models.Gaussian1D() + models.Polynomial1D(degree=1)
|
||||
```
|
||||
|
||||
**Available fitters**:
|
||||
- `LinearLSQFitter` - Linear least squares (fast, for linear models)
|
||||
- `LevMarLSQFitter` - Levenberg-Marquardt (most common)
|
||||
- `SimplexLSQFitter` - Downhill simplex
|
||||
- `SLSQPLSQFitter` - Sequential Least Squares with constraints
|
||||
|
||||
### 8. World Coordinate System (WCS)
|
||||
|
||||
Transform between pixel and world coordinates in images.
|
||||
|
||||
**Basic WCS usage**:
|
||||
```python
|
||||
from astropy.io import fits
|
||||
from astropy.wcs import WCS
|
||||
|
||||
# Read FITS with WCS
|
||||
hdu = fits.open('image.fits')[0]
|
||||
wcs = WCS(hdu.header)
|
||||
|
||||
# Pixel to world
|
||||
ra, dec = wcs.pixel_to_world_values(100, 200)
|
||||
|
||||
# World to pixel
|
||||
x, y = wcs.world_to_pixel_values(ra, dec)
|
||||
|
||||
# Using SkyCoord (more powerful)
|
||||
from astropy.coordinates import SkyCoord
|
||||
import astropy.units as u
|
||||
|
||||
coord = SkyCoord(ra=150*u.deg, dec=-30*u.deg)
|
||||
x, y = wcs.world_to_pixel(coord)
|
||||
```
|
||||
|
||||
**Plotting with WCS**:
|
||||
```python
|
||||
from astropy.io import fits
|
||||
from astropy.wcs import WCS
|
||||
from astropy.visualization import ImageNormalize, LogStretch, PercentileInterval
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
hdu = fits.open('image.fits')[0]
|
||||
wcs = WCS(hdu.header)
|
||||
data = hdu.data
|
||||
|
||||
# Create figure with WCS projection
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111, projection=wcs)
|
||||
|
||||
# Plot with coordinate grid
|
||||
norm = ImageNormalize(data, interval=PercentileInterval(99.5),
|
||||
stretch=LogStretch())
|
||||
ax.imshow(data, norm=norm, origin='lower', cmap='viridis')
|
||||
|
||||
# Coordinate labels and grid
|
||||
ax.set_xlabel('RA')
|
||||
ax.set_ylabel('Dec')
|
||||
ax.coords.grid(color='white', alpha=0.5)
|
||||
```
|
||||
|
||||
### 9. Statistics and Data Processing
|
||||
|
||||
Robust statistical tools for astronomical data.
|
||||
|
||||
**Sigma clipping** (remove outliers):
|
||||
```python
|
||||
from astropy.stats import sigma_clip, sigma_clipped_stats
|
||||
|
||||
# Remove outliers
|
||||
clipped = sigma_clip(data, sigma=3, maxiters=5)
|
||||
|
||||
# Get statistics on cleaned data
|
||||
mean, median, std = sigma_clipped_stats(data, sigma=3)
|
||||
|
||||
# Use clipped data
|
||||
background = median
|
||||
signal = data - background
|
||||
snr = signal / std
|
||||
```
|
||||
|
||||
**Other statistical functions**:
|
||||
```python
|
||||
from astropy.stats import mad_std, biweight_location, biweight_scale
|
||||
|
||||
# Robust standard deviation
|
||||
std_robust = mad_std(data)
|
||||
|
||||
# Robust central location
|
||||
center = biweight_location(data)
|
||||
|
||||
# Robust scale
|
||||
scale = biweight_scale(data)
|
||||
```
|
||||
|
||||
### 10. Visualization
|
||||
|
||||
Display astronomical images with proper scaling.
|
||||
|
||||
**Image normalization and stretching**:
|
||||
```python
|
||||
from astropy.visualization import (ImageNormalize, MinMaxInterval,
|
||||
PercentileInterval, ZScaleInterval,
|
||||
SqrtStretch, LogStretch, PowerStretch,
|
||||
AsinhStretch)
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Common combination: percentile interval + sqrt stretch
|
||||
norm = ImageNormalize(data,
|
||||
interval=PercentileInterval(99),
|
||||
stretch=SqrtStretch())
|
||||
|
||||
plt.imshow(data, norm=norm, origin='lower', cmap='gray')
|
||||
plt.colorbar()
|
||||
```
|
||||
|
||||
**Available intervals** (determine min/max):
|
||||
- `MinMaxInterval()` - Use actual min/max
|
||||
- `PercentileInterval(percentile)` - Clip to percentile (e.g., 99%)
|
||||
- `ZScaleInterval()` - IRAF's zscale algorithm
|
||||
- `ManualInterval(vmin, vmax)` - Specify manually
|
||||
|
||||
**Available stretches** (nonlinear scaling):
|
||||
- `LinearStretch()` - Linear (default)
|
||||
- `SqrtStretch()` - Square root (common for images)
|
||||
- `LogStretch()` - Logarithmic (for high dynamic range)
|
||||
- `PowerStretch(power)` - Power law
|
||||
- `AsinhStretch()` - Arcsinh (good for wide range)
|
||||
|
||||
## Bundled Resources
|
||||
|
||||
### scripts/
|
||||
|
||||
**`fits_info.py`** - Comprehensive FITS file inspection tool
|
||||
```bash
|
||||
python scripts/fits_info.py observation.fits
|
||||
python scripts/fits_info.py observation.fits --detailed
|
||||
python scripts/fits_info.py observation.fits --ext 1
|
||||
```
|
||||
|
||||
**`coord_convert.py`** - Batch coordinate transformation utility
|
||||
```bash
|
||||
python scripts/coord_convert.py 10.68 41.27 --from icrs --to galactic
|
||||
python scripts/coord_convert.py --file coords.txt --from icrs --to galactic
|
||||
```
|
||||
|
||||
**`cosmo_calc.py`** - Cosmological calculator
|
||||
```bash
|
||||
python scripts/cosmo_calc.py 0.5 1.0 1.5
|
||||
python scripts/cosmo_calc.py --range 0 3 0.5 --cosmology Planck18
|
||||
```
|
||||
|
||||
### references/
|
||||
|
||||
**`module_overview.md`** - Comprehensive reference of all astropy subpackages, classes, and methods. Consult this for detailed API information, available functions, and module capabilities.
|
||||
|
||||
**`common_workflows.md`** - Complete working examples for common astronomical data analysis tasks. Contains full code examples for FITS operations, coordinate transformations, cosmology, modeling, and complete analysis pipelines.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use context managers for FITS files**:
|
||||
```python
|
||||
with fits.open('file.fits') as hdul:
|
||||
# Work with file
|
||||
```
|
||||
|
||||
2. **Prefer astropy.table over raw FITS tables** for better unit/metadata support
|
||||
|
||||
3. **Use SkyCoord for coordinates** (high-level interface) rather than low-level frame classes
|
||||
|
||||
4. **Always attach units** to quantities when possible for dimensional safety
|
||||
|
||||
5. **Use ECSV format** for saving tables when you want to preserve units and metadata
|
||||
|
||||
6. **Vectorize coordinate operations** rather than looping for performance
|
||||
|
||||
7. **Use memmap=True** when opening large FITS files to save memory
|
||||
|
||||
8. **Install Bottleneck** package for faster statistics operations
|
||||
|
||||
9. **Pre-compute composite units** for repeated operations to improve performance
|
||||
|
||||
10. **Consult `references/module_overview.md`** for detailed module information and `references/common_workflows.md`** for complete working examples
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Pattern: FITS → Process → FITS
|
||||
```python
|
||||
from astropy.io import fits
|
||||
from astropy.stats import sigma_clipped_stats
|
||||
|
||||
# Read
|
||||
with fits.open('input.fits') as hdul:
|
||||
data = hdul[0].data
|
||||
header = hdul[0].header
|
||||
|
||||
# Process
|
||||
mean, median, std = sigma_clipped_stats(data, sigma=3)
|
||||
processed = (data - median) / std
|
||||
|
||||
# Write
|
||||
fits.writeto('output.fits', processed, header, overwrite=True)
|
||||
```
|
||||
|
||||
### Pattern: Catalog Matching
|
||||
```python
|
||||
from astropy.coordinates import SkyCoord
|
||||
from astropy.table import Table
|
||||
import astropy.units as u
|
||||
|
||||
# Load catalogs
|
||||
cat1 = Table.read('catalog1.fits')
|
||||
cat2 = Table.read('catalog2.fits')
|
||||
|
||||
# Create coordinate objects
|
||||
coords1 = SkyCoord(ra=cat1['RA'], dec=cat1['DEC'], unit=u.degree)
|
||||
coords2 = SkyCoord(ra=cat2['RA'], dec=cat2['DEC'], unit=u.degree)
|
||||
|
||||
# Match
|
||||
idx, sep2d, dist3d = coords1.match_to_catalog_sky(coords2)
|
||||
|
||||
# Filter by separation
|
||||
max_sep = 1 * u.arcsec
|
||||
matched_mask = sep2d < max_sep
|
||||
|
||||
# Create matched catalog
|
||||
matched_cat1 = cat1[matched_mask]
|
||||
matched_cat2 = cat2[idx[matched_mask]]
|
||||
```
|
||||
|
||||
### Pattern: Time Series Analysis
|
||||
```python
|
||||
from astropy.time import Time
|
||||
from astropy.timeseries import TimeSeries
|
||||
import astropy.units as u
|
||||
|
||||
# Create time series
|
||||
times = Time(['2023-01-01', '2023-01-02', '2023-01-03'])
|
||||
flux = [1.2, 2.3, 1.8] * u.Jy
|
||||
|
||||
ts = TimeSeries(time=times)
|
||||
ts['flux'] = flux
|
||||
|
||||
# Fold on period
|
||||
from astropy.timeseries import aggregate_downsample
|
||||
period = 1.5 * u.day
|
||||
folded = ts.fold(period=period)
|
||||
```
|
||||
|
||||
### Pattern: Image Display with WCS
|
||||
```python
|
||||
from astropy.io import fits
|
||||
from astropy.wcs import WCS
|
||||
from astropy.visualization import ImageNormalize, SqrtStretch, PercentileInterval
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
hdu = fits.open('image.fits')[0]
|
||||
wcs = WCS(hdu.header)
|
||||
data = hdu.data
|
||||
|
||||
fig = plt.figure(figsize=(10, 10))
|
||||
ax = fig.add_subplot(111, projection=wcs)
|
||||
|
||||
norm = ImageNormalize(data, interval=PercentileInterval(99),
|
||||
stretch=SqrtStretch())
|
||||
im = ax.imshow(data, norm=norm, origin='lower', cmap='viridis')
|
||||
|
||||
ax.set_xlabel('RA')
|
||||
ax.set_ylabel('Dec')
|
||||
ax.coords.grid(color='white', alpha=0.5, linestyle='solid')
|
||||
plt.colorbar(im, ax=ax)
|
||||
```
|
||||
|
||||
## Installation Note
|
||||
|
||||
Ensure astropy is installed in the Python environment:
|
||||
```bash
|
||||
pip install astropy
|
||||
```
|
||||
|
||||
For additional performance and features:
|
||||
```bash
|
||||
pip install astropy[all] # Includes optional dependencies
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- Official documentation: https://docs.astropy.org
|
||||
- Tutorials: https://learn.astropy.org
|
||||
- API reference: Consult `references/module_overview.md` in this skill
|
||||
- Working examples: Consult `references/common_workflows.md` in this skill
|
||||
618
scientific-packages/astropy/references/common_workflows.md
Normal file
618
scientific-packages/astropy/references/common_workflows.md
Normal file
@@ -0,0 +1,618 @@
|
||||
# Common Astropy Workflows
|
||||
|
||||
This document describes frequently used workflows when working with astronomical data using astropy.
|
||||
|
||||
## 1. Working with FITS Files
|
||||
|
||||
### Basic FITS Reading
|
||||
```python
|
||||
from astropy.io import fits
|
||||
import numpy as np
|
||||
|
||||
# Open and examine structure
|
||||
with fits.open('observation.fits') as hdul:
|
||||
hdul.info()
|
||||
|
||||
# Access primary HDU
|
||||
primary_hdr = hdul[0].header
|
||||
primary_data = hdul[0].data
|
||||
|
||||
# Access extension
|
||||
ext_data = hdul[1].data
|
||||
ext_hdr = hdul[1].header
|
||||
|
||||
# Read specific header keywords
|
||||
object_name = primary_hdr['OBJECT']
|
||||
exposure = primary_hdr['EXPTIME']
|
||||
```
|
||||
|
||||
### Writing FITS Files
|
||||
```python
|
||||
# Create new FITS file
|
||||
from astropy.io import fits
|
||||
import numpy as np
|
||||
|
||||
# Create data
|
||||
data = np.random.random((100, 100))
|
||||
|
||||
# Create primary HDU
|
||||
hdu = fits.PrimaryHDU(data)
|
||||
hdu.header['OBJECT'] = 'M31'
|
||||
hdu.header['EXPTIME'] = 300.0
|
||||
|
||||
# Write to file
|
||||
hdu.writeto('output.fits', overwrite=True)
|
||||
|
||||
# Multi-extension FITS
|
||||
hdul = fits.HDUList([
|
||||
fits.PrimaryHDU(data1),
|
||||
fits.ImageHDU(data2, name='SCI'),
|
||||
fits.ImageHDU(data3, name='ERR')
|
||||
])
|
||||
hdul.writeto('multi_ext.fits', overwrite=True)
|
||||
```
|
||||
|
||||
### FITS Table Operations
|
||||
```python
|
||||
from astropy.io import fits
|
||||
|
||||
# Read binary table
|
||||
with fits.open('catalog.fits') as hdul:
|
||||
table_data = hdul[1].data
|
||||
|
||||
# Access columns
|
||||
ra = table_data['RA']
|
||||
dec = table_data['DEC']
|
||||
mag = table_data['MAG']
|
||||
|
||||
# Filter data
|
||||
bright = table_data[table_data['MAG'] < 15]
|
||||
|
||||
# Write binary table
|
||||
from astropy.table import Table
|
||||
import astropy.units as u
|
||||
|
||||
t = Table([ra, dec, mag], names=['RA', 'DEC', 'MAG'])
|
||||
t['RA'].unit = u.degree
|
||||
t['DEC'].unit = u.degree
|
||||
t.write('output_catalog.fits', format='fits', overwrite=True)
|
||||
```
|
||||
|
||||
## 2. Coordinate Transformations
|
||||
|
||||
### Basic Coordinate Creation and Transformation
|
||||
```python
|
||||
from astropy.coordinates import SkyCoord
|
||||
import astropy.units as u
|
||||
|
||||
# Create from RA/Dec
|
||||
c = SkyCoord(ra=10.68458*u.degree, dec=41.26917*u.degree, frame='icrs')
|
||||
|
||||
# Alternative creation methods
|
||||
c = SkyCoord('00:42:44.3 +41:16:09', unit=(u.hourangle, u.deg))
|
||||
c = SkyCoord('00h42m44.3s +41d16m09s')
|
||||
|
||||
# Transform to different frames
|
||||
c_gal = c.galactic
|
||||
c_fk5 = c.fk5
|
||||
print(f"Galactic: l={c_gal.l.deg}, b={c_gal.b.deg}")
|
||||
```
|
||||
|
||||
### Coordinate Arrays and Separations
|
||||
```python
|
||||
import numpy as np
|
||||
from astropy.coordinates import SkyCoord
|
||||
import astropy.units as u
|
||||
|
||||
# Create array of coordinates
|
||||
ra_array = np.array([10.1, 10.2, 10.3]) * u.degree
|
||||
dec_array = np.array([40.1, 40.2, 40.3]) * u.degree
|
||||
coords = SkyCoord(ra=ra_array, dec=dec_array, frame='icrs')
|
||||
|
||||
# Calculate separations
|
||||
c1 = SkyCoord(ra=10*u.degree, dec=40*u.degree)
|
||||
c2 = SkyCoord(ra=11*u.degree, dec=41*u.degree)
|
||||
sep = c1.separation(c2)
|
||||
print(f"Separation: {sep.to(u.arcmin)}")
|
||||
|
||||
# Position angle
|
||||
pa = c1.position_angle(c2)
|
||||
```
|
||||
|
||||
### Catalog Matching
|
||||
```python
|
||||
from astropy.coordinates import SkyCoord, match_coordinates_sky
|
||||
import astropy.units as u
|
||||
|
||||
# Two catalogs of coordinates
|
||||
catalog1 = SkyCoord(ra=[10, 11, 12]*u.degree, dec=[40, 41, 42]*u.degree)
|
||||
catalog2 = SkyCoord(ra=[10.01, 11.02, 13]*u.degree, dec=[40.01, 41.01, 43]*u.degree)
|
||||
|
||||
# Find nearest neighbors
|
||||
idx, sep2d, dist3d = catalog1.match_to_catalog_sky(catalog2)
|
||||
|
||||
# Filter by separation threshold
|
||||
max_sep = 1 * u.arcsec
|
||||
matched = sep2d < max_sep
|
||||
matching_indices = idx[matched]
|
||||
```
|
||||
|
||||
### Horizontal Coordinates (Alt/Az)
|
||||
```python
|
||||
from astropy.coordinates import SkyCoord, EarthLocation, AltAz
|
||||
from astropy.time import Time
|
||||
import astropy.units as u
|
||||
|
||||
# Observer location
|
||||
location = EarthLocation(lat=40*u.deg, lon=-70*u.deg, height=300*u.m)
|
||||
|
||||
# Observation time
|
||||
obstime = Time('2023-01-01 03:00:00')
|
||||
|
||||
# Target coordinate
|
||||
target = SkyCoord(ra=10*u.degree, dec=40*u.degree, frame='icrs')
|
||||
|
||||
# Transform to Alt/Az
|
||||
altaz_frame = AltAz(obstime=obstime, location=location)
|
||||
target_altaz = target.transform_to(altaz_frame)
|
||||
|
||||
print(f"Altitude: {target_altaz.alt.deg}")
|
||||
print(f"Azimuth: {target_altaz.az.deg}")
|
||||
```
|
||||
|
||||
## 3. Units and Quantities
|
||||
|
||||
### Basic Unit Operations
|
||||
```python
|
||||
import astropy.units as u
|
||||
|
||||
# Create quantities
|
||||
distance = 5.2 * u.parsec
|
||||
time = 10 * u.year
|
||||
velocity = 300 * u.km / u.s
|
||||
|
||||
# Unit conversion
|
||||
distance_ly = distance.to(u.lightyear)
|
||||
velocity_mps = velocity.to(u.m / u.s)
|
||||
|
||||
# Arithmetic with units
|
||||
wavelength = 500 * u.nm
|
||||
frequency = wavelength.to(u.Hz, equivalencies=u.spectral())
|
||||
|
||||
# Compose/decompose units
|
||||
composite = (1 * u.kg * u.m**2 / u.s**2)
|
||||
print(composite.decompose()) # Base SI units
|
||||
print(composite.compose()) # Known compound units (Joule)
|
||||
```
|
||||
|
||||
### Working with Arrays
|
||||
```python
|
||||
import numpy as np
|
||||
import astropy.units as u
|
||||
|
||||
# Quantity arrays
|
||||
wavelengths = np.array([400, 500, 600]) * u.nm
|
||||
frequencies = wavelengths.to(u.THz, equivalencies=u.spectral())
|
||||
|
||||
# Mathematical operations preserve units
|
||||
fluxes = np.array([1.2, 2.3, 1.8]) * u.Jy
|
||||
luminosities = 4 * np.pi * (10*u.pc)**2 * fluxes
|
||||
```
|
||||
|
||||
### Custom Units and Equivalencies
|
||||
```python
|
||||
import astropy.units as u
|
||||
|
||||
# Define custom unit
|
||||
beam = u.def_unit('beam', 1.5e-10 * u.steradian)
|
||||
|
||||
# Register for session
|
||||
u.add_enabled_units([beam])
|
||||
|
||||
# Use in calculations
|
||||
flux_per_beam = 1.5 * u.Jy / beam
|
||||
|
||||
# Doppler equivalencies
|
||||
rest_wavelength = 656.3 * u.nm # H-alpha
|
||||
observed = 656.5 * u.nm
|
||||
velocity = observed.to(u.km/u.s,
|
||||
equivalencies=u.doppler_optical(rest_wavelength))
|
||||
```
|
||||
|
||||
## 4. Time Handling
|
||||
|
||||
### Time Creation and Conversion
|
||||
```python
|
||||
from astropy.time import Time
|
||||
import astropy.units as u
|
||||
|
||||
# Create time objects
|
||||
t1 = Time('2023-01-01T00:00:00', format='isot', scale='utc')
|
||||
t2 = Time(2459945.5, format='jd', scale='utc')
|
||||
t3 = Time(['2023-01-01', '2023-06-01'], format='iso')
|
||||
|
||||
# Convert formats
|
||||
print(t1.jd) # Julian Date
|
||||
print(t1.mjd) # Modified Julian Date
|
||||
print(t1.unix) # Unix timestamp
|
||||
print(t1.iso) # ISO format
|
||||
|
||||
# Convert time scales
|
||||
print(t1.tai) # Convert to TAI
|
||||
print(t1.tt) # Convert to TT
|
||||
print(t1.tdb) # Convert to TDB
|
||||
```
|
||||
|
||||
### Time Arithmetic
|
||||
```python
|
||||
from astropy.time import Time, TimeDelta
|
||||
import astropy.units as u
|
||||
|
||||
t1 = Time('2023-01-01T00:00:00')
|
||||
dt = TimeDelta(1*u.day)
|
||||
|
||||
# Add time delta
|
||||
t2 = t1 + dt
|
||||
|
||||
# Difference between times
|
||||
diff = t2 - t1
|
||||
print(diff.to(u.hour))
|
||||
|
||||
# Array of times
|
||||
times = t1 + np.arange(10) * u.day
|
||||
```
|
||||
|
||||
### Sidereal Time and Astronomical Calculations
|
||||
```python
|
||||
from astropy.time import Time
|
||||
from astropy.coordinates import EarthLocation
|
||||
import astropy.units as u
|
||||
|
||||
location = EarthLocation(lat=40*u.deg, lon=-70*u.deg)
|
||||
t = Time('2023-01-01T00:00:00')
|
||||
|
||||
# Local sidereal time
|
||||
lst = t.sidereal_time('apparent', longitude=location.lon)
|
||||
|
||||
# Light travel time correction
|
||||
from astropy.coordinates import SkyCoord
|
||||
target = SkyCoord(ra=10*u.deg, dec=40*u.deg)
|
||||
ltt_bary = t.light_travel_time(target, location=location)
|
||||
t_bary = t + ltt_bary
|
||||
```
|
||||
|
||||
## 5. Tables and Data Management
|
||||
|
||||
### Creating and Manipulating Tables
|
||||
```python
|
||||
from astropy.table import Table, Column
|
||||
import astropy.units as u
|
||||
import numpy as np
|
||||
|
||||
# Create table
|
||||
t = Table()
|
||||
t['name'] = ['Star1', 'Star2', 'Star3']
|
||||
t['ra'] = [10.5, 11.2, 12.3] * u.degree
|
||||
t['dec'] = [41.2, 42.1, 43.5] * u.degree
|
||||
t['flux'] = [1.2, 2.3, 0.8] * u.Jy
|
||||
|
||||
# Add column metadata
|
||||
t['flux'].description = 'Flux at 1.4 GHz'
|
||||
t['flux'].format = '.2f'
|
||||
|
||||
# Add new column
|
||||
t['flux_mJy'] = t['flux'].to(u.mJy)
|
||||
|
||||
# Filter rows
|
||||
bright = t[t['flux'] > 1.0 * u.Jy]
|
||||
|
||||
# Sort
|
||||
t.sort('flux')
|
||||
```
|
||||
|
||||
### Table I/O
|
||||
```python
|
||||
from astropy.table import Table
|
||||
|
||||
# Read various formats
|
||||
t = Table.read('data.fits')
|
||||
t = Table.read('data.csv', format='ascii.csv')
|
||||
t = Table.read('data.ecsv', format='ascii.ecsv') # Preserves units
|
||||
t = Table.read('data.votable', format='votable')
|
||||
|
||||
# Write various formats
|
||||
t.write('output.fits', overwrite=True)
|
||||
t.write('output.csv', format='ascii.csv', overwrite=True)
|
||||
t.write('output.ecsv', format='ascii.ecsv', overwrite=True)
|
||||
t.write('output.votable', format='votable', overwrite=True)
|
||||
```
|
||||
|
||||
### Advanced Table Operations
|
||||
```python
|
||||
from astropy.table import Table, join, vstack, hstack
|
||||
|
||||
# Join tables
|
||||
t1 = Table([[1, 2], ['a', 'b']], names=['id', 'val1'])
|
||||
t2 = Table([[1, 2], ['c', 'd']], names=['id', 'val2'])
|
||||
joined = join(t1, t2, keys='id')
|
||||
|
||||
# Stack tables vertically
|
||||
combined = vstack([t1, t2])
|
||||
|
||||
# Stack horizontally
|
||||
combined = hstack([t1, t2])
|
||||
|
||||
# Grouping
|
||||
t.group_by('category').groups.aggregate(np.mean)
|
||||
```
|
||||
|
||||
### Tables with Astronomical Objects
|
||||
```python
|
||||
from astropy.table import Table
|
||||
from astropy.coordinates import SkyCoord
|
||||
from astropy.time import Time
|
||||
import astropy.units as u
|
||||
|
||||
# Table with SkyCoord column
|
||||
coords = SkyCoord(ra=[10, 11, 12]*u.deg, dec=[40, 41, 42]*u.deg)
|
||||
times = Time(['2023-01-01', '2023-01-02', '2023-01-03'])
|
||||
|
||||
t = Table([coords, times], names=['coords', 'obstime'])
|
||||
|
||||
# Access individual coordinates
|
||||
print(t['coords'][0].ra)
|
||||
print(t['coords'][0].dec)
|
||||
```
|
||||
|
||||
## 6. Cosmological Calculations
|
||||
|
||||
### Distance Calculations
|
||||
```python
|
||||
from astropy.cosmology import Planck18, FlatLambdaCDM
|
||||
import astropy.units as u
|
||||
import numpy as np
|
||||
|
||||
# Use built-in cosmology
|
||||
cosmo = Planck18
|
||||
|
||||
# Redshifts
|
||||
z = np.linspace(0, 5, 50)
|
||||
|
||||
# Calculate distances
|
||||
comoving_dist = cosmo.comoving_distance(z)
|
||||
angular_diam_dist = cosmo.angular_diameter_distance(z)
|
||||
luminosity_dist = cosmo.luminosity_distance(z)
|
||||
|
||||
# Age of universe
|
||||
age_at_z = cosmo.age(z)
|
||||
lookback_time = cosmo.lookback_time(z)
|
||||
|
||||
# Hubble parameter
|
||||
H_z = cosmo.H(z)
|
||||
```
|
||||
|
||||
### Converting Observables
|
||||
```python
|
||||
from astropy.cosmology import Planck18
|
||||
import astropy.units as u
|
||||
|
||||
cosmo = Planck18
|
||||
z = 1.5
|
||||
|
||||
# Angular diameter distance
|
||||
d_A = cosmo.angular_diameter_distance(z)
|
||||
|
||||
# Convert angular size to physical size
|
||||
angular_size = 1 * u.arcsec
|
||||
physical_size = (angular_size.to(u.radian) * d_A).to(u.kpc)
|
||||
|
||||
# Convert flux to luminosity
|
||||
flux = 1e-17 * u.erg / u.s / u.cm**2
|
||||
d_L = cosmo.luminosity_distance(z)
|
||||
luminosity = flux * 4 * np.pi * d_L**2
|
||||
|
||||
# Find redshift for given distance
|
||||
from astropy.cosmology import z_at_value
|
||||
z_result = z_at_value(cosmo.luminosity_distance, 1000*u.Mpc)
|
||||
```
|
||||
|
||||
### Custom Cosmology
|
||||
```python
|
||||
from astropy.cosmology import FlatLambdaCDM
|
||||
import astropy.units as u
|
||||
|
||||
# Define custom cosmology
|
||||
my_cosmo = FlatLambdaCDM(H0=70 * u.km/u.s/u.Mpc,
|
||||
Om0=0.3,
|
||||
Tcmb0=2.725 * u.K)
|
||||
|
||||
# Use it for calculations
|
||||
print(my_cosmo.age(0))
|
||||
print(my_cosmo.luminosity_distance(1.5))
|
||||
```
|
||||
|
||||
## 7. Model Fitting
|
||||
|
||||
### Fitting 1D Models
|
||||
```python
|
||||
from astropy.modeling import models, fitting
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Generate data with noise
|
||||
x = np.linspace(0, 10, 100)
|
||||
true_model = models.Gaussian1D(amplitude=10, mean=5, stddev=1)
|
||||
y = true_model(x) + np.random.normal(0, 0.5, x.shape)
|
||||
|
||||
# Create and fit model
|
||||
g_init = models.Gaussian1D(amplitude=8, mean=4.5, stddev=0.8)
|
||||
fitter = fitting.LevMarLSQFitter()
|
||||
g_fit = fitter(g_init, x, y)
|
||||
|
||||
# Plot results
|
||||
plt.plot(x, y, 'o', label='Data')
|
||||
plt.plot(x, g_fit(x), label='Fit')
|
||||
plt.legend()
|
||||
|
||||
# Get fitted parameters
|
||||
print(f"Amplitude: {g_fit.amplitude.value}")
|
||||
print(f"Mean: {g_fit.mean.value}")
|
||||
print(f"Stddev: {g_fit.stddev.value}")
|
||||
```
|
||||
|
||||
### Fitting with Constraints
|
||||
```python
|
||||
from astropy.modeling import models, fitting
|
||||
|
||||
# Set parameter bounds
|
||||
g = models.Gaussian1D(amplitude=10, mean=5, stddev=1)
|
||||
g.amplitude.bounds = (0, None) # Positive only
|
||||
g.mean.bounds = (4, 6) # Constrain center
|
||||
g.stddev.fixed = True # Fix width
|
||||
|
||||
# Tie parameters (for multi-component models)
|
||||
g1 = models.Gaussian1D(amplitude=10, mean=5, stddev=1, name='g1')
|
||||
g2 = models.Gaussian1D(amplitude=5, mean=6, stddev=1, name='g2')
|
||||
g2.stddev.tied = lambda model: model.g1.stddev
|
||||
|
||||
# Compound model
|
||||
model = g1 + g2
|
||||
```
|
||||
|
||||
### 2D Image Fitting
|
||||
```python
|
||||
from astropy.modeling import models, fitting
|
||||
import numpy as np
|
||||
|
||||
# Create 2D data
|
||||
y, x = np.mgrid[0:100, 0:100]
|
||||
z = models.Gaussian2D(amplitude=100, x_mean=50, y_mean=50,
|
||||
x_stddev=5, y_stddev=5)(x, y)
|
||||
z += np.random.normal(0, 5, z.shape)
|
||||
|
||||
# Fit 2D Gaussian
|
||||
g_init = models.Gaussian2D(amplitude=90, x_mean=48, y_mean=48,
|
||||
x_stddev=4, y_stddev=4)
|
||||
fitter = fitting.LevMarLSQFitter()
|
||||
g_fit = fitter(g_init, x, y, z)
|
||||
|
||||
# Get parameters
|
||||
print(f"Center: ({g_fit.x_mean.value}, {g_fit.y_mean.value})")
|
||||
print(f"Width: ({g_fit.x_stddev.value}, {g_fit.y_stddev.value})")
|
||||
```
|
||||
|
||||
## 8. Image Processing and Visualization
|
||||
|
||||
### Image Display with Proper Scaling
|
||||
```python
|
||||
from astropy.io import fits
|
||||
from astropy.visualization import ImageNormalize, SqrtStretch, PercentileInterval
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Read FITS image
|
||||
data = fits.getdata('image.fits')
|
||||
|
||||
# Apply normalization
|
||||
norm = ImageNormalize(data,
|
||||
interval=PercentileInterval(99),
|
||||
stretch=SqrtStretch())
|
||||
|
||||
# Display
|
||||
plt.imshow(data, norm=norm, origin='lower', cmap='gray')
|
||||
plt.colorbar()
|
||||
```
|
||||
|
||||
### WCS Plotting
|
||||
```python
|
||||
from astropy.io import fits
|
||||
from astropy.wcs import WCS
|
||||
from astropy.visualization import ImageNormalize, LogStretch, PercentileInterval
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Read FITS with WCS
|
||||
hdu = fits.open('image.fits')[0]
|
||||
wcs = WCS(hdu.header)
|
||||
data = hdu.data
|
||||
|
||||
# Create figure with WCS projection
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111, projection=wcs)
|
||||
|
||||
# Plot with coordinate grid
|
||||
norm = ImageNormalize(data, interval=PercentileInterval(99.5),
|
||||
stretch=LogStretch())
|
||||
im = ax.imshow(data, norm=norm, origin='lower', cmap='viridis')
|
||||
|
||||
# Add coordinate labels
|
||||
ax.set_xlabel('RA')
|
||||
ax.set_ylabel('Dec')
|
||||
ax.coords.grid(color='white', alpha=0.5)
|
||||
plt.colorbar(im)
|
||||
```
|
||||
|
||||
### Sigma Clipping and Statistics
|
||||
```python
|
||||
from astropy.stats import sigma_clip, sigma_clipped_stats
|
||||
import numpy as np
|
||||
|
||||
# Data with outliers
|
||||
data = np.random.normal(100, 15, 1000)
|
||||
data[0:50] = np.random.normal(200, 10, 50) # Add outliers
|
||||
|
||||
# Sigma clipping
|
||||
clipped = sigma_clip(data, sigma=3, maxiters=5)
|
||||
|
||||
# Get statistics on clipped data
|
||||
mean, median, std = sigma_clipped_stats(data, sigma=3)
|
||||
|
||||
print(f"Mean: {mean:.2f}")
|
||||
print(f"Median: {median:.2f}")
|
||||
print(f"Std: {std:.2f}")
|
||||
print(f"Clipped {clipped.mask.sum()} values")
|
||||
```
|
||||
|
||||
## 9. Complete Analysis Example
|
||||
|
||||
### Photometry Pipeline
|
||||
```python
|
||||
from astropy.io import fits
|
||||
from astropy.wcs import WCS
|
||||
from astropy.coordinates import SkyCoord
|
||||
from astropy.stats import sigma_clipped_stats
|
||||
from astropy.visualization import ImageNormalize, LogStretch
|
||||
import astropy.units as u
|
||||
import numpy as np
|
||||
|
||||
# Read FITS file
|
||||
hdu = fits.open('observation.fits')[0]
|
||||
data = hdu.data
|
||||
header = hdu.header
|
||||
wcs = WCS(header)
|
||||
|
||||
# Calculate background statistics
|
||||
mean, median, std = sigma_clipped_stats(data, sigma=3.0)
|
||||
print(f"Background: {median:.2f} +/- {std:.2f}")
|
||||
|
||||
# Subtract background
|
||||
data_sub = data - median
|
||||
|
||||
# Known source coordinates
|
||||
source_coord = SkyCoord(ra='10:42:30', dec='+41:16:09', unit=(u.hourangle, u.deg))
|
||||
|
||||
# Convert to pixel coordinates
|
||||
x_pix, y_pix = wcs.world_to_pixel(source_coord)
|
||||
|
||||
# Simple aperture photometry
|
||||
aperture_radius = 10 # pixels
|
||||
y, x = np.ogrid[:data.shape[0], :data.shape[1]]
|
||||
mask = (x - x_pix)**2 + (y - y_pix)**2 <= aperture_radius**2
|
||||
|
||||
aperture_sum = np.sum(data_sub[mask])
|
||||
npix = np.sum(mask)
|
||||
|
||||
print(f"Source position: ({x_pix:.1f}, {y_pix:.1f})")
|
||||
print(f"Aperture sum: {aperture_sum:.2f}")
|
||||
print(f"S/N: {aperture_sum / (std * np.sqrt(npix)):.2f}")
|
||||
```
|
||||
|
||||
This workflow document provides practical examples for common astronomical data analysis tasks using astropy.
|
||||
340
scientific-packages/astropy/references/module_overview.md
Normal file
340
scientific-packages/astropy/references/module_overview.md
Normal file
@@ -0,0 +1,340 @@
|
||||
# Astropy Module Overview
|
||||
|
||||
This document provides a comprehensive reference of all major astropy subpackages and their capabilities.
|
||||
|
||||
## Core Data Structures
|
||||
|
||||
### astropy.units
|
||||
**Purpose**: Handle physical units and dimensional analysis in computations.
|
||||
|
||||
**Key Classes**:
|
||||
- `Quantity` - Combines numerical values with units
|
||||
- `Unit` - Represents physical units
|
||||
|
||||
**Common Operations**:
|
||||
```python
|
||||
import astropy.units as u
|
||||
distance = 5 * u.meter
|
||||
time = 2 * u.second
|
||||
velocity = distance / time # Returns Quantity in m/s
|
||||
wavelength = 500 * u.nm
|
||||
frequency = wavelength.to(u.Hz, equivalencies=u.spectral())
|
||||
```
|
||||
|
||||
**Equivalencies**:
|
||||
- `u.spectral()` - Convert wavelength ↔ frequency
|
||||
- `u.doppler_optical()`, `u.doppler_radio()` - Velocity conversions
|
||||
- `u.temperature()` - Temperature unit conversions
|
||||
- `u.pixel_scale()` - Pixel to physical units
|
||||
|
||||
### astropy.constants
|
||||
**Purpose**: Provide physical and astronomical constants.
|
||||
|
||||
**Common Constants**:
|
||||
- `c` - Speed of light
|
||||
- `G` - Gravitational constant
|
||||
- `h` - Planck constant
|
||||
- `M_sun`, `R_sun`, `L_sun` - Solar mass, radius, luminosity
|
||||
- `M_earth`, `R_earth` - Earth mass, radius
|
||||
- `pc`, `au` - Parsec, astronomical unit
|
||||
|
||||
### astropy.time
|
||||
**Purpose**: Represent and manipulate times and dates with astronomical precision.
|
||||
|
||||
**Time Scales**:
|
||||
- `UTC` - Coordinated Universal Time
|
||||
- `TAI` - International Atomic Time
|
||||
- `TT` - Terrestrial Time
|
||||
- `TCB`, `TCG` - Barycentric/Geocentric Coordinate Time
|
||||
- `TDB` - Barycentric Dynamical Time
|
||||
- `UT1` - Universal Time
|
||||
|
||||
**Common Formats**:
|
||||
- `iso`, `isot` - ISO 8601 strings
|
||||
- `jd`, `mjd` - Julian/Modified Julian Date
|
||||
- `unix`, `gps` - Unix/GPS timestamps
|
||||
- `datetime` - Python datetime objects
|
||||
|
||||
**Example**:
|
||||
```python
|
||||
from astropy.time import Time
|
||||
t = Time('2023-01-01T00:00:00', format='isot', scale='utc')
|
||||
print(t.mjd) # Modified Julian Date
|
||||
print(t.jd) # Julian Date
|
||||
print(t.tt) # Convert to TT scale
|
||||
```
|
||||
|
||||
### astropy.table
|
||||
**Purpose**: Work with tabular data optimized for astronomical applications.
|
||||
|
||||
**Key Features**:
|
||||
- Native support for astropy Quantity, Time, and SkyCoord columns
|
||||
- Multi-dimensional columns
|
||||
- Metadata preservation (units, descriptions, formats)
|
||||
- Advanced operations: joins, grouping, binning
|
||||
- File I/O via unified interface
|
||||
|
||||
**Example**:
|
||||
```python
|
||||
from astropy.table import Table
|
||||
import astropy.units as u
|
||||
|
||||
t = Table()
|
||||
t['name'] = ['Star1', 'Star2', 'Star3']
|
||||
t['ra'] = [10.5, 11.2, 12.3] * u.degree
|
||||
t['dec'] = [41.2, 42.1, 43.5] * u.degree
|
||||
t['flux'] = [1.2, 2.3, 0.8] * u.Jy
|
||||
```
|
||||
|
||||
## Coordinates and World Coordinate Systems
|
||||
|
||||
### astropy.coordinates
|
||||
**Purpose**: Represent and transform celestial coordinates.
|
||||
|
||||
**Primary Interface**: `SkyCoord` - High-level class for sky positions
|
||||
|
||||
**Coordinate Frames**:
|
||||
- `ICRS` - International Celestial Reference System (default)
|
||||
- `FK5`, `FK4` - Fifth/Fourth Fundamental Katalog
|
||||
- `Galactic`, `Supergalactic` - Galactic coordinates
|
||||
- `AltAz` - Horizontal (altitude-azimuth) coordinates
|
||||
- `GCRS`, `CIRS`, `ITRS` - Earth-based systems
|
||||
- `BarycentricMeanEcliptic`, `HeliocentricMeanEcliptic`, `GeocentricMeanEcliptic` - Ecliptic coordinates
|
||||
|
||||
**Common Operations**:
|
||||
```python
|
||||
from astropy.coordinates import SkyCoord
|
||||
import astropy.units as u
|
||||
|
||||
# Create coordinate
|
||||
c = SkyCoord(ra=10.625*u.degree, dec=41.2*u.degree, frame='icrs')
|
||||
|
||||
# Transform to galactic
|
||||
c_gal = c.galactic
|
||||
|
||||
# Calculate separation
|
||||
c2 = SkyCoord(ra=11*u.degree, dec=42*u.degree, frame='icrs')
|
||||
sep = c.separation(c2)
|
||||
|
||||
# Match catalogs
|
||||
idx, sep2d, dist3d = c.match_to_catalog_sky(catalog_coords)
|
||||
```
|
||||
|
||||
### astropy.wcs
|
||||
**Purpose**: Handle World Coordinate System transformations for astronomical images.
|
||||
|
||||
**Key Class**: `WCS` - Maps between pixel and world coordinates
|
||||
|
||||
**Common Use Cases**:
|
||||
- Convert pixel coordinates to RA/Dec
|
||||
- Convert RA/Dec to pixel coordinates
|
||||
- Handle distortion corrections (SIP, lookup tables)
|
||||
|
||||
**Example**:
|
||||
```python
|
||||
from astropy.wcs import WCS
|
||||
from astropy.io import fits
|
||||
|
||||
hdu = fits.open('image.fits')[0]
|
||||
wcs = WCS(hdu.header)
|
||||
|
||||
# Pixel to world
|
||||
ra, dec = wcs.pixel_to_world_values(100, 200)
|
||||
|
||||
# World to pixel
|
||||
x, y = wcs.world_to_pixel_values(ra, dec)
|
||||
```
|
||||
|
||||
## File I/O
|
||||
|
||||
### astropy.io.fits
|
||||
**Purpose**: Read and write FITS (Flexible Image Transport System) files.
|
||||
|
||||
**Key Classes**:
|
||||
- `HDUList` - Container for all HDUs in a file
|
||||
- `PrimaryHDU` - Primary header data unit
|
||||
- `ImageHDU` - Image extension
|
||||
- `BinTableHDU` - Binary table extension
|
||||
- `Header` - FITS header keywords
|
||||
|
||||
**Common Operations**:
|
||||
```python
|
||||
from astropy.io import fits
|
||||
|
||||
# Read FITS file
|
||||
with fits.open('file.fits') as hdul:
|
||||
hdul.info() # Display structure
|
||||
header = hdul[0].header
|
||||
data = hdul[0].data
|
||||
|
||||
# Write FITS file
|
||||
fits.writeto('output.fits', data, header)
|
||||
|
||||
# Update header keyword
|
||||
fits.setval('file.fits', 'OBJECT', value='M31')
|
||||
```
|
||||
|
||||
### astropy.io.ascii
|
||||
**Purpose**: Read and write ASCII tables in various formats.
|
||||
|
||||
**Supported Formats**:
|
||||
- Basic, CSV, tab-delimited
|
||||
- CDS/MRT (Machine Readable Tables)
|
||||
- IPAC, Daophot, SExtractor
|
||||
- LaTeX tables
|
||||
- HTML tables
|
||||
|
||||
### astropy.io.votable
|
||||
**Purpose**: Handle Virtual Observatory (VO) table format.
|
||||
|
||||
### astropy.io.misc
|
||||
**Purpose**: Additional formats including HDF5, Parquet, and YAML.
|
||||
|
||||
## Scientific Calculations
|
||||
|
||||
### astropy.cosmology
|
||||
**Purpose**: Perform cosmological calculations.
|
||||
|
||||
**Common Models**:
|
||||
- `FlatLambdaCDM` - Flat universe with cosmological constant (most common)
|
||||
- `LambdaCDM` - Universe with cosmological constant
|
||||
- `Planck18`, `Planck15`, `Planck13` - Pre-defined Planck parameters
|
||||
- `WMAP9`, `WMAP7`, `WMAP5` - Pre-defined WMAP parameters
|
||||
|
||||
**Common Methods**:
|
||||
```python
|
||||
from astropy.cosmology import FlatLambdaCDM, Planck18
|
||||
import astropy.units as u
|
||||
|
||||
cosmo = FlatLambdaCDM(H0=70, Om0=0.3)
|
||||
# Or use built-in: cosmo = Planck18
|
||||
|
||||
z = 1.5
|
||||
print(cosmo.age(z)) # Age of universe at z
|
||||
print(cosmo.luminosity_distance(z)) # Luminosity distance
|
||||
print(cosmo.angular_diameter_distance(z)) # Angular diameter distance
|
||||
print(cosmo.comoving_distance(z)) # Comoving distance
|
||||
print(cosmo.H(z)) # Hubble parameter at z
|
||||
```
|
||||
|
||||
### astropy.modeling
|
||||
**Purpose**: Framework for model evaluation and fitting.
|
||||
|
||||
**Model Categories**:
|
||||
- 1D models: Gaussian1D, Lorentz1D, Voigt1D, Polynomial1D
|
||||
- 2D models: Gaussian2D, Disk2D, Moffat2D
|
||||
- Physical models: BlackBody, Drude1D, NFW
|
||||
- Polynomial models: Chebyshev, Legendre
|
||||
|
||||
**Common Fitters**:
|
||||
- `LinearLSQFitter` - Linear least squares
|
||||
- `LevMarLSQFitter` - Levenberg-Marquardt
|
||||
- `SimplexLSQFitter` - Downhill simplex
|
||||
|
||||
**Example**:
|
||||
```python
|
||||
from astropy.modeling import models, fitting
|
||||
|
||||
# Create model
|
||||
g = models.Gaussian1D(amplitude=10, mean=5, stddev=1)
|
||||
|
||||
# Fit to data
|
||||
fitter = fitting.LevMarLSQFitter()
|
||||
fitted_model = fitter(g, x_data, y_data)
|
||||
```
|
||||
|
||||
### astropy.convolution
|
||||
**Purpose**: Convolve and filter astronomical data.
|
||||
|
||||
**Common Kernels**:
|
||||
- `Gaussian2DKernel` - 2D Gaussian smoothing
|
||||
- `Box2DKernel` - 2D boxcar smoothing
|
||||
- `Tophat2DKernel` - 2D tophat filter
|
||||
- Custom kernels via arrays
|
||||
|
||||
### astropy.stats
|
||||
**Purpose**: Statistical tools for astronomical data analysis.
|
||||
|
||||
**Key Functions**:
|
||||
- `sigma_clip()` - Remove outliers via sigma clipping
|
||||
- `sigma_clipped_stats()` - Compute mean, median, std with clipping
|
||||
- `mad_std()` - Median Absolute Deviation
|
||||
- `biweight_location()`, `biweight_scale()` - Robust statistics
|
||||
- `circmean()`, `circstd()` - Circular statistics
|
||||
|
||||
**Example**:
|
||||
```python
|
||||
from astropy.stats import sigma_clip, sigma_clipped_stats
|
||||
|
||||
# Remove outliers
|
||||
filtered_data = sigma_clip(data, sigma=3, maxiters=5)
|
||||
|
||||
# Get statistics
|
||||
mean, median, std = sigma_clipped_stats(data, sigma=3)
|
||||
```
|
||||
|
||||
## Data Processing
|
||||
|
||||
### astropy.nddata
|
||||
**Purpose**: Handle N-dimensional datasets with metadata.
|
||||
|
||||
**Key Class**: `NDData` - Container for array data with units, uncertainty, mask, and WCS
|
||||
|
||||
### astropy.timeseries
|
||||
**Purpose**: Work with time series data.
|
||||
|
||||
**Key Classes**:
|
||||
- `TimeSeries` - Time-indexed data table
|
||||
- `BinnedTimeSeries` - Time-binned data
|
||||
|
||||
**Common Operations**:
|
||||
- Period finding (Lomb-Scargle)
|
||||
- Folding time series
|
||||
- Binning data
|
||||
|
||||
### astropy.visualization
|
||||
**Purpose**: Display astronomical data effectively.
|
||||
|
||||
**Key Features**:
|
||||
- Image normalization (LogStretch, PowerStretch, SqrtStretch, etc.)
|
||||
- Interval scaling (MinMaxInterval, PercentileInterval, ZScaleInterval)
|
||||
- WCSAxes for plotting with coordinate overlays
|
||||
- RGB image creation with stretching
|
||||
- Astronomical colormaps
|
||||
|
||||
**Example**:
|
||||
```python
|
||||
from astropy.visualization import ImageNormalize, SqrtStretch, PercentileInterval
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
norm = ImageNormalize(data, interval=PercentileInterval(99),
|
||||
stretch=SqrtStretch())
|
||||
plt.imshow(data, norm=norm, origin='lower')
|
||||
```
|
||||
|
||||
## Utilities
|
||||
|
||||
### astropy.samp
|
||||
**Purpose**: Simple Application Messaging Protocol for inter-application communication.
|
||||
|
||||
**Use Case**: Connect Python scripts with other astronomical tools (e.g., DS9, TOPCAT).
|
||||
|
||||
## Module Import Patterns
|
||||
|
||||
**Standard imports**:
|
||||
```python
|
||||
import astropy.units as u
|
||||
from astropy.coordinates import SkyCoord
|
||||
from astropy.time import Time
|
||||
from astropy.io import fits
|
||||
from astropy.table import Table
|
||||
from astropy import constants as const
|
||||
```
|
||||
|
||||
## Performance Tips
|
||||
|
||||
1. **Pre-compute composite units** for repeated operations
|
||||
2. **Use `<<` operator** for fast unit assignments: `array << u.m` instead of `array * u.m`
|
||||
3. **Vectorize operations** rather than looping over coordinates/times
|
||||
4. **Use memmap=True** when opening large FITS files
|
||||
5. **Install Bottleneck** for faster stats operations
|
||||
226
scientific-packages/astropy/scripts/coord_convert.py
Normal file
226
scientific-packages/astropy/scripts/coord_convert.py
Normal file
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Coordinate conversion utility for astronomical coordinates.
|
||||
|
||||
This script provides batch coordinate transformations between different
|
||||
astronomical coordinate systems using astropy.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
from astropy.coordinates import SkyCoord
|
||||
import astropy.units as u
|
||||
|
||||
|
||||
def convert_coordinates(coords_input, input_frame='icrs', output_frame='galactic',
|
||||
input_format='decimal', output_format='decimal'):
|
||||
"""
|
||||
Convert astronomical coordinates between different frames.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
coords_input : list of tuples or str
|
||||
Input coordinates as (lon, lat) pairs or strings
|
||||
input_frame : str
|
||||
Input coordinate frame (icrs, fk5, galactic, etc.)
|
||||
output_frame : str
|
||||
Output coordinate frame
|
||||
input_format : str
|
||||
Format of input coordinates ('decimal', 'sexagesimal', 'hourangle')
|
||||
output_format : str
|
||||
Format for output display ('decimal', 'sexagesimal', 'hourangle')
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
Converted coordinates
|
||||
"""
|
||||
results = []
|
||||
|
||||
for coord in coords_input:
|
||||
try:
|
||||
# Parse input coordinate
|
||||
if input_format == 'decimal':
|
||||
if isinstance(coord, str):
|
||||
parts = coord.split()
|
||||
lon, lat = float(parts[0]), float(parts[1])
|
||||
else:
|
||||
lon, lat = coord
|
||||
c = SkyCoord(lon*u.degree, lat*u.degree, frame=input_frame)
|
||||
|
||||
elif input_format == 'sexagesimal':
|
||||
c = SkyCoord(coord, frame=input_frame, unit=(u.hourangle, u.deg))
|
||||
|
||||
elif input_format == 'hourangle':
|
||||
if isinstance(coord, str):
|
||||
parts = coord.split()
|
||||
lon, lat = parts[0], parts[1]
|
||||
else:
|
||||
lon, lat = coord
|
||||
c = SkyCoord(lon, lat, frame=input_frame, unit=(u.hourangle, u.deg))
|
||||
|
||||
# Transform to output frame
|
||||
if output_frame == 'icrs':
|
||||
c_out = c.icrs
|
||||
elif output_frame == 'fk5':
|
||||
c_out = c.fk5
|
||||
elif output_frame == 'fk4':
|
||||
c_out = c.fk4
|
||||
elif output_frame == 'galactic':
|
||||
c_out = c.galactic
|
||||
elif output_frame == 'supergalactic':
|
||||
c_out = c.supergalactic
|
||||
else:
|
||||
c_out = c.transform_to(output_frame)
|
||||
|
||||
results.append(c_out)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error converting coordinate {coord}: {e}", file=sys.stderr)
|
||||
results.append(None)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def format_output(coords, frame, output_format='decimal'):
|
||||
"""Format coordinates for display."""
|
||||
output = []
|
||||
|
||||
for c in coords:
|
||||
if c is None:
|
||||
output.append("ERROR")
|
||||
continue
|
||||
|
||||
if frame in ['icrs', 'fk5', 'fk4']:
|
||||
lon_name, lat_name = 'RA', 'Dec'
|
||||
lon = c.ra
|
||||
lat = c.dec
|
||||
elif frame == 'galactic':
|
||||
lon_name, lat_name = 'l', 'b'
|
||||
lon = c.l
|
||||
lat = c.b
|
||||
elif frame == 'supergalactic':
|
||||
lon_name, lat_name = 'sgl', 'sgb'
|
||||
lon = c.sgl
|
||||
lat = c.sgb
|
||||
else:
|
||||
lon_name, lat_name = 'lon', 'lat'
|
||||
lon = c.spherical.lon
|
||||
lat = c.spherical.lat
|
||||
|
||||
if output_format == 'decimal':
|
||||
out_str = f"{lon.degree:12.8f} {lat.degree:+12.8f}"
|
||||
elif output_format == 'sexagesimal':
|
||||
if frame in ['icrs', 'fk5', 'fk4']:
|
||||
out_str = f"{lon.to_string(unit=u.hourangle, sep=':', pad=True)} "
|
||||
out_str += f"{lat.to_string(unit=u.degree, sep=':', pad=True)}"
|
||||
else:
|
||||
out_str = f"{lon.to_string(unit=u.degree, sep=':', pad=True)} "
|
||||
out_str += f"{lat.to_string(unit=u.degree, sep=':', pad=True)}"
|
||||
elif output_format == 'hourangle':
|
||||
out_str = f"{lon.to_string(unit=u.hourangle, sep=' ', pad=True)} "
|
||||
out_str += f"{lat.to_string(unit=u.degree, sep=' ', pad=True)}"
|
||||
|
||||
output.append(out_str)
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function for command-line usage."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Convert astronomical coordinates between different frames',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Supported frames: icrs, fk5, fk4, galactic, supergalactic
|
||||
|
||||
Input formats:
|
||||
decimal : Degrees (e.g., "10.68 41.27")
|
||||
sexagesimal : HMS/DMS (e.g., "00:42:44.3 +41:16:09")
|
||||
hourangle : Hours and degrees (e.g., "10.5h 41.5d")
|
||||
|
||||
Examples:
|
||||
%(prog)s --from icrs --to galactic "10.68 41.27"
|
||||
%(prog)s --from icrs --to galactic --input decimal --output sexagesimal "150.5 -30.2"
|
||||
%(prog)s --from galactic --to icrs "120.5 45.3"
|
||||
%(prog)s --file coords.txt --from icrs --to galactic
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument('coordinates', nargs='*',
|
||||
help='Coordinates to convert (lon lat pairs)')
|
||||
parser.add_argument('-f', '--from', dest='input_frame', default='icrs',
|
||||
help='Input coordinate frame (default: icrs)')
|
||||
parser.add_argument('-t', '--to', dest='output_frame', default='galactic',
|
||||
help='Output coordinate frame (default: galactic)')
|
||||
parser.add_argument('-i', '--input', dest='input_format', default='decimal',
|
||||
choices=['decimal', 'sexagesimal', 'hourangle'],
|
||||
help='Input format (default: decimal)')
|
||||
parser.add_argument('-o', '--output', dest='output_format', default='decimal',
|
||||
choices=['decimal', 'sexagesimal', 'hourangle'],
|
||||
help='Output format (default: decimal)')
|
||||
parser.add_argument('--file', dest='input_file',
|
||||
help='Read coordinates from file (one per line)')
|
||||
parser.add_argument('--header', action='store_true',
|
||||
help='Print header line with coordinate names')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get coordinates from file or command line
|
||||
if args.input_file:
|
||||
try:
|
||||
with open(args.input_file, 'r') as f:
|
||||
coords = [line.strip() for line in f if line.strip()]
|
||||
except FileNotFoundError:
|
||||
print(f"Error: File '{args.input_file}' not found.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
if not args.coordinates:
|
||||
print("Error: No coordinates provided.", file=sys.stderr)
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
# Combine pairs of arguments
|
||||
if args.input_format == 'decimal':
|
||||
coords = []
|
||||
i = 0
|
||||
while i < len(args.coordinates):
|
||||
if i + 1 < len(args.coordinates):
|
||||
coords.append(f"{args.coordinates[i]} {args.coordinates[i+1]}")
|
||||
i += 2
|
||||
else:
|
||||
print(f"Warning: Odd number of coordinates, skipping last value",
|
||||
file=sys.stderr)
|
||||
break
|
||||
else:
|
||||
coords = args.coordinates
|
||||
|
||||
# Convert coordinates
|
||||
converted = convert_coordinates(coords,
|
||||
input_frame=args.input_frame,
|
||||
output_frame=args.output_frame,
|
||||
input_format=args.input_format,
|
||||
output_format=args.output_format)
|
||||
|
||||
# Format and print output
|
||||
formatted = format_output(converted, args.output_frame, args.output_format)
|
||||
|
||||
# Print header if requested
|
||||
if args.header:
|
||||
if args.output_frame in ['icrs', 'fk5', 'fk4']:
|
||||
if args.output_format == 'decimal':
|
||||
print(f"{'RA (deg)':>12s} {'Dec (deg)':>13s}")
|
||||
else:
|
||||
print(f"{'RA':>25s} {'Dec':>26s}")
|
||||
elif args.output_frame == 'galactic':
|
||||
if args.output_format == 'decimal':
|
||||
print(f"{'l (deg)':>12s} {'b (deg)':>13s}")
|
||||
else:
|
||||
print(f"{'l':>25s} {'b':>26s}")
|
||||
|
||||
for line in formatted:
|
||||
print(line)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
250
scientific-packages/astropy/scripts/cosmo_calc.py
Normal file
250
scientific-packages/astropy/scripts/cosmo_calc.py
Normal file
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Cosmological calculator using astropy.cosmology.
|
||||
|
||||
This script provides quick calculations of cosmological distances,
|
||||
ages, and other quantities for given redshifts.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import numpy as np
|
||||
from astropy.cosmology import FlatLambdaCDM, Planck18, Planck15, WMAP9
|
||||
import astropy.units as u
|
||||
|
||||
|
||||
def calculate_cosmology(redshifts, cosmology='Planck18', H0=None, Om0=None):
|
||||
"""
|
||||
Calculate cosmological quantities for given redshifts.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
redshifts : array-like
|
||||
Redshift values
|
||||
cosmology : str
|
||||
Cosmology to use ('Planck18', 'Planck15', 'WMAP9', 'custom')
|
||||
H0 : float, optional
|
||||
Hubble constant for custom cosmology (km/s/Mpc)
|
||||
Om0 : float, optional
|
||||
Matter density parameter for custom cosmology
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
Dictionary containing calculated quantities
|
||||
"""
|
||||
# Select cosmology
|
||||
if cosmology == 'Planck18':
|
||||
cosmo = Planck18
|
||||
elif cosmology == 'Planck15':
|
||||
cosmo = Planck15
|
||||
elif cosmology == 'WMAP9':
|
||||
cosmo = WMAP9
|
||||
elif cosmology == 'custom':
|
||||
if H0 is None or Om0 is None:
|
||||
raise ValueError("Must provide H0 and Om0 for custom cosmology")
|
||||
cosmo = FlatLambdaCDM(H0=H0 * u.km/u.s/u.Mpc, Om0=Om0)
|
||||
else:
|
||||
raise ValueError(f"Unknown cosmology: {cosmology}")
|
||||
|
||||
z = np.atleast_1d(redshifts)
|
||||
|
||||
results = {
|
||||
'redshift': z,
|
||||
'cosmology': str(cosmo),
|
||||
'luminosity_distance': cosmo.luminosity_distance(z),
|
||||
'angular_diameter_distance': cosmo.angular_diameter_distance(z),
|
||||
'comoving_distance': cosmo.comoving_distance(z),
|
||||
'comoving_volume': cosmo.comoving_volume(z),
|
||||
'age': cosmo.age(z),
|
||||
'lookback_time': cosmo.lookback_time(z),
|
||||
'H': cosmo.H(z),
|
||||
'scale_factor': 1.0 / (1.0 + z)
|
||||
}
|
||||
|
||||
return results, cosmo
|
||||
|
||||
|
||||
def print_results(results, verbose=False, csv=False):
|
||||
"""Print calculation results."""
|
||||
|
||||
z = results['redshift']
|
||||
|
||||
if csv:
|
||||
# CSV output
|
||||
print("z,D_L(Mpc),D_A(Mpc),D_C(Mpc),Age(Gyr),t_lookback(Gyr),H(km/s/Mpc)")
|
||||
for i in range(len(z)):
|
||||
print(f"{z[i]:.6f},"
|
||||
f"{results['luminosity_distance'][i].value:.6f},"
|
||||
f"{results['angular_diameter_distance'][i].value:.6f},"
|
||||
f"{results['comoving_distance'][i].value:.6f},"
|
||||
f"{results['age'][i].value:.6f},"
|
||||
f"{results['lookback_time'][i].value:.6f},"
|
||||
f"{results['H'][i].value:.6f}")
|
||||
else:
|
||||
# Formatted table output
|
||||
if verbose:
|
||||
print(f"\nCosmology: {results['cosmology']}")
|
||||
print("-" * 80)
|
||||
|
||||
print(f"\n{'z':>8s} {'D_L':>12s} {'D_A':>12s} {'D_C':>12s} "
|
||||
f"{'Age':>10s} {'t_lb':>10s} {'H(z)':>10s}")
|
||||
print(f"{'':>8s} {'(Mpc)':>12s} {'(Mpc)':>12s} {'(Mpc)':>12s} "
|
||||
f"{'(Gyr)':>10s} {'(Gyr)':>10s} {'(km/s/Mpc)':>10s}")
|
||||
print("-" * 80)
|
||||
|
||||
for i in range(len(z)):
|
||||
print(f"{z[i]:8.4f} "
|
||||
f"{results['luminosity_distance'][i].value:12.3f} "
|
||||
f"{results['angular_diameter_distance'][i].value:12.3f} "
|
||||
f"{results['comoving_distance'][i].value:12.3f} "
|
||||
f"{results['age'][i].value:10.4f} "
|
||||
f"{results['lookback_time'][i].value:10.4f} "
|
||||
f"{results['H'][i].value:10.4f}")
|
||||
|
||||
if verbose:
|
||||
print("\nLegend:")
|
||||
print(" z : Redshift")
|
||||
print(" D_L : Luminosity distance")
|
||||
print(" D_A : Angular diameter distance")
|
||||
print(" D_C : Comoving distance")
|
||||
print(" Age : Age of universe at z")
|
||||
print(" t_lb : Lookback time to z")
|
||||
print(" H(z) : Hubble parameter at z")
|
||||
|
||||
|
||||
def convert_quantity(value, quantity_type, cosmo, to_redshift=False):
|
||||
"""
|
||||
Convert between redshift and cosmological quantity.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : float
|
||||
Value to convert
|
||||
quantity_type : str
|
||||
Type of quantity ('luminosity_distance', 'age', etc.)
|
||||
cosmo : Cosmology
|
||||
Cosmology object
|
||||
to_redshift : bool
|
||||
If True, convert quantity to redshift; else convert z to quantity
|
||||
"""
|
||||
from astropy.cosmology import z_at_value
|
||||
|
||||
if to_redshift:
|
||||
# Convert quantity to redshift
|
||||
if quantity_type == 'luminosity_distance':
|
||||
z = z_at_value(cosmo.luminosity_distance, value * u.Mpc)
|
||||
elif quantity_type == 'age':
|
||||
z = z_at_value(cosmo.age, value * u.Gyr)
|
||||
elif quantity_type == 'lookback_time':
|
||||
z = z_at_value(cosmo.lookback_time, value * u.Gyr)
|
||||
elif quantity_type == 'comoving_distance':
|
||||
z = z_at_value(cosmo.comoving_distance, value * u.Mpc)
|
||||
else:
|
||||
raise ValueError(f"Unknown quantity type: {quantity_type}")
|
||||
return z
|
||||
else:
|
||||
# Convert redshift to quantity
|
||||
if quantity_type == 'luminosity_distance':
|
||||
return cosmo.luminosity_distance(value)
|
||||
elif quantity_type == 'age':
|
||||
return cosmo.age(value)
|
||||
elif quantity_type == 'lookback_time':
|
||||
return cosmo.lookback_time(value)
|
||||
elif quantity_type == 'comoving_distance':
|
||||
return cosmo.comoving_distance(value)
|
||||
else:
|
||||
raise ValueError(f"Unknown quantity type: {quantity_type}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function for command-line usage."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Calculate cosmological quantities for given redshifts',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Available cosmologies: Planck18, Planck15, WMAP9, custom
|
||||
|
||||
Examples:
|
||||
%(prog)s 0.5 1.0 1.5
|
||||
%(prog)s 0.5 --cosmology Planck15
|
||||
%(prog)s 0.5 --cosmology custom --H0 70 --Om0 0.3
|
||||
%(prog)s --range 0 3 0.5
|
||||
%(prog)s 0.5 --verbose
|
||||
%(prog)s 0.5 1.0 --csv
|
||||
%(prog)s --convert 1000 --from luminosity_distance --cosmology Planck18
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument('redshifts', nargs='*', type=float,
|
||||
help='Redshift values to calculate')
|
||||
parser.add_argument('-c', '--cosmology', default='Planck18',
|
||||
choices=['Planck18', 'Planck15', 'WMAP9', 'custom'],
|
||||
help='Cosmology to use (default: Planck18)')
|
||||
parser.add_argument('--H0', type=float,
|
||||
help='Hubble constant for custom cosmology (km/s/Mpc)')
|
||||
parser.add_argument('--Om0', type=float,
|
||||
help='Matter density parameter for custom cosmology')
|
||||
parser.add_argument('-r', '--range', nargs=3, type=float, metavar=('START', 'STOP', 'STEP'),
|
||||
help='Generate redshift range (start stop step)')
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='Print verbose output with cosmology details')
|
||||
parser.add_argument('--csv', action='store_true',
|
||||
help='Output in CSV format')
|
||||
parser.add_argument('--convert', type=float,
|
||||
help='Convert a quantity to redshift')
|
||||
parser.add_argument('--from', dest='from_quantity',
|
||||
choices=['luminosity_distance', 'age', 'lookback_time', 'comoving_distance'],
|
||||
help='Type of quantity to convert from')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Handle conversion mode
|
||||
if args.convert is not None:
|
||||
if args.from_quantity is None:
|
||||
print("Error: Must specify --from when using --convert", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Get cosmology
|
||||
if args.cosmology == 'Planck18':
|
||||
cosmo = Planck18
|
||||
elif args.cosmology == 'Planck15':
|
||||
cosmo = Planck15
|
||||
elif args.cosmology == 'WMAP9':
|
||||
cosmo = WMAP9
|
||||
elif args.cosmology == 'custom':
|
||||
if args.H0 is None or args.Om0 is None:
|
||||
print("Error: Must provide --H0 and --Om0 for custom cosmology",
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
cosmo = FlatLambdaCDM(H0=args.H0 * u.km/u.s/u.Mpc, Om0=args.Om0)
|
||||
|
||||
z = convert_quantity(args.convert, args.from_quantity, cosmo, to_redshift=True)
|
||||
print(f"\n{args.from_quantity.replace('_', ' ').title()} = {args.convert}")
|
||||
print(f"Redshift z = {z:.6f}")
|
||||
print(f"(using {args.cosmology} cosmology)")
|
||||
return
|
||||
|
||||
# Get redshifts
|
||||
if args.range:
|
||||
start, stop, step = args.range
|
||||
redshifts = np.arange(start, stop + step/2, step)
|
||||
elif args.redshifts:
|
||||
redshifts = np.array(args.redshifts)
|
||||
else:
|
||||
print("Error: No redshifts provided.", file=sys.stderr)
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
# Calculate
|
||||
try:
|
||||
results, cosmo = calculate_cosmology(redshifts, args.cosmology,
|
||||
H0=args.H0, Om0=args.Om0)
|
||||
print_results(results, verbose=args.verbose, csv=args.csv)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
189
scientific-packages/astropy/scripts/fits_info.py
Normal file
189
scientific-packages/astropy/scripts/fits_info.py
Normal file
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Quick FITS file inspection tool.
|
||||
|
||||
This script provides a convenient way to inspect FITS file structure,
|
||||
headers, and basic statistics without writing custom code each time.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from astropy.io import fits
|
||||
import numpy as np
|
||||
|
||||
|
||||
def print_fits_info(filename, detailed=False, ext=None):
|
||||
"""
|
||||
Print comprehensive information about a FITS file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
Path to FITS file
|
||||
detailed : bool
|
||||
If True, print detailed statistics for each HDU
|
||||
ext : int or str, optional
|
||||
Specific extension to examine in detail
|
||||
"""
|
||||
print(f"\n{'='*70}")
|
||||
print(f"FITS File: {filename}")
|
||||
print(f"{'='*70}\n")
|
||||
|
||||
try:
|
||||
with fits.open(filename) as hdul:
|
||||
# Print file structure
|
||||
print("File Structure:")
|
||||
print("-" * 70)
|
||||
hdul.info()
|
||||
print()
|
||||
|
||||
# If specific extension requested
|
||||
if ext is not None:
|
||||
print(f"\nDetailed view of extension: {ext}")
|
||||
print("-" * 70)
|
||||
hdu = hdul[ext]
|
||||
print_hdu_details(hdu, detailed=True)
|
||||
return
|
||||
|
||||
# Print header and data info for each HDU
|
||||
for i, hdu in enumerate(hdul):
|
||||
print(f"\n{'='*70}")
|
||||
print(f"HDU {i}: {hdu.name}")
|
||||
print(f"{'='*70}")
|
||||
print_hdu_details(hdu, detailed=detailed)
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"Error: File '{filename}' not found.")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"Error reading FITS file: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def print_hdu_details(hdu, detailed=False):
|
||||
"""Print details for a single HDU."""
|
||||
|
||||
# Header information
|
||||
print("\nHeader Information:")
|
||||
print("-" * 70)
|
||||
|
||||
# Key header keywords
|
||||
important_keywords = ['SIMPLE', 'BITPIX', 'NAXIS', 'EXTEND',
|
||||
'OBJECT', 'TELESCOP', 'INSTRUME', 'OBSERVER',
|
||||
'DATE-OBS', 'EXPTIME', 'FILTER', 'AIRMASS',
|
||||
'RA', 'DEC', 'EQUINOX', 'CTYPE1', 'CTYPE2']
|
||||
|
||||
header = hdu.header
|
||||
for key in important_keywords:
|
||||
if key in header:
|
||||
value = header[key]
|
||||
comment = header.comments[key]
|
||||
print(f" {key:12s} = {str(value):20s} / {comment}")
|
||||
|
||||
# NAXIS keywords
|
||||
if 'NAXIS' in header:
|
||||
naxis = header['NAXIS']
|
||||
for i in range(1, naxis + 1):
|
||||
key = f'NAXIS{i}'
|
||||
if key in header:
|
||||
print(f" {key:12s} = {str(header[key]):20s} / {header.comments[key]}")
|
||||
|
||||
# Data information
|
||||
if hdu.data is not None:
|
||||
print("\nData Information:")
|
||||
print("-" * 70)
|
||||
|
||||
data = hdu.data
|
||||
print(f" Data type: {data.dtype}")
|
||||
print(f" Shape: {data.shape}")
|
||||
|
||||
# For image data
|
||||
if hasattr(data, 'ndim') and data.ndim >= 1:
|
||||
try:
|
||||
# Calculate statistics
|
||||
finite_data = data[np.isfinite(data)]
|
||||
if len(finite_data) > 0:
|
||||
print(f" Min: {np.min(finite_data):.6g}")
|
||||
print(f" Max: {np.max(finite_data):.6g}")
|
||||
print(f" Mean: {np.mean(finite_data):.6g}")
|
||||
print(f" Median: {np.median(finite_data):.6g}")
|
||||
print(f" Std: {np.std(finite_data):.6g}")
|
||||
|
||||
# Count special values
|
||||
n_nan = np.sum(np.isnan(data))
|
||||
n_inf = np.sum(np.isinf(data))
|
||||
if n_nan > 0:
|
||||
print(f" NaN values: {n_nan}")
|
||||
if n_inf > 0:
|
||||
print(f" Inf values: {n_inf}")
|
||||
except Exception as e:
|
||||
print(f" Could not calculate statistics: {e}")
|
||||
|
||||
# For table data
|
||||
if hasattr(data, 'columns'):
|
||||
print(f"\n Table Columns ({len(data.columns)}):")
|
||||
for col in data.columns:
|
||||
print(f" {col.name:20s} {col.format:10s} {col.unit or ''}")
|
||||
|
||||
if detailed:
|
||||
print(f"\n First few rows:")
|
||||
print(data[:min(5, len(data))])
|
||||
else:
|
||||
print("\n No data in this HDU")
|
||||
|
||||
# WCS information if present
|
||||
try:
|
||||
from astropy.wcs import WCS
|
||||
wcs = WCS(hdu.header)
|
||||
if wcs.has_celestial:
|
||||
print("\nWCS Information:")
|
||||
print("-" * 70)
|
||||
print(f" Has celestial WCS: Yes")
|
||||
print(f" CTYPE: {wcs.wcs.ctype}")
|
||||
if wcs.wcs.crval is not None:
|
||||
print(f" CRVAL: {wcs.wcs.crval}")
|
||||
if wcs.wcs.crpix is not None:
|
||||
print(f" CRPIX: {wcs.wcs.crpix}")
|
||||
if wcs.wcs.cdelt is not None:
|
||||
print(f" CDELT: {wcs.wcs.cdelt}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function for command-line usage."""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Inspect FITS file structure and contents',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
%(prog)s image.fits
|
||||
%(prog)s image.fits --detailed
|
||||
%(prog)s image.fits --ext 1
|
||||
%(prog)s image.fits --ext SCI
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument('filename', help='FITS file to inspect')
|
||||
parser.add_argument('-d', '--detailed', action='store_true',
|
||||
help='Show detailed statistics for each HDU')
|
||||
parser.add_argument('-e', '--ext', type=str, default=None,
|
||||
help='Show details for specific extension only (number or name)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Convert extension to int if numeric
|
||||
ext = args.ext
|
||||
if ext is not None:
|
||||
try:
|
||||
ext = int(ext)
|
||||
except ValueError:
|
||||
pass # Keep as string for extension name
|
||||
|
||||
print_fits_info(args.filename, detailed=args.detailed, ext=ext)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user