mirror of
https://github.com/K-Dense-AI/claude-scientific-skills.git
synced 2026-01-26 16:58:56 +08:00
Add more scientific skills
This commit is contained in:
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