A personal arsenal of reusable code for solar physics research.
이 레포지터리는 개인 프로젝트들에서 공통으로 사용하는 유틸리티를 모아둔 라이브러리입니다. 일부 내용은 직접 작성했으며, 일부는 AI의 도움을 받아 작성되었습니다.
pip install git+https://github.com/eunsu-park/egghouse.gitFor development:
git clone https://github.com/eunsu-park/egghouse.git
cd egghouse
pip install -e .| Module | Required | Optional |
|---|---|---|
| Core | numpy, scipy, pandas | - |
| io | - | astropy |
| image | numpy, scipy | - |
| sdo | numpy, scipy | astropy, sunpy |
| dem | numpy | aiapy, astropy, sunpy |
| config | pyyaml | - |
| database | psycopg2-binary | pyyaml |
| transfer | requests, beautifulsoup4 | paramiko (SFTP) |
# 1. FITS 파일 읽기/쓰기
from egghouse.io import read_fits, write_fits
data, header = read_fits('image.fits')
write_fits('output.fits', data, overwrite=True)
# astropy 없이 FITS 읽기 (pure numpy)
from egghouse.io import read_fits_simple
data, header = read_fits_simple('image.fits')
# 2. 이미지 전처리 (resize, mask, scaling)
from egghouse.image import resize_image, circle_mask, bytescale_image
resized = resize_image(data, (1024, 1024))
mask = circle_mask(4096, radius=1600)
display = bytescale_image(data, imin=0, imax=5000)
# 3. SDO Level 1.5 변환
from egghouse.sdo import to_level15
level15_map = to_level15('aia_lev1.fits') # auto-detect AIA/HMI
# 4. DEM (Differential Emission Measure) 분석
from egghouse.sdo import dem_sites, get_temperature_response, get_default_temperatures
temps = get_default_temperatures(n_bins=50)
response = get_temperature_response(temperatures=temps)
dem, info = dem_sites(intensities, errors, response, temps)File I/O for scientific data formats (FITS, BMP).
from egghouse.io import read_fits, write_fits, read_fits_header, append_fits
# FITS read/write
data, header = read_fits('image.fits')
header = read_fits_header('image.fits') # header only (memory-efficient)
write_fits('output.fits', data, header={'OBJECT': 'Sun'}, overwrite=True)
append_fits('output.fits', ext_data, header={'EXTNAME': 'EXT1'})
# BMP read/write (no external dependencies)
from egghouse.io import read_bmp, write_bmp
data, info = read_bmp('image.bmp') # (H, W, 3) uint8 RGB
write_bmp('output.bmp', data) # 24-bit RGB
write_bmp('gray.bmp', gray_uint8) # 8-bit grayscaleGeneric image processing utilities.
from egghouse.image import resize_image, rotate_image, bytescale_image
from egghouse.image import circle_mask, annulus_mask, pad_image, crop_or_pad
# Resize/Rotate (preserves dtype)
resized = resize_image(image, (1024, 1024), order=1)
rotated = rotate_image(image, angle=45, reshape=False)
# Scale to uint8 for display
display = bytescale_image(data, imin=0, imax=5000)
# Masks for solar disk analysis
disk_mask = circle_mask(4096, radius=1600)
corona_mask = annulus_mask(4096, inner_radius=1600, outer_radius=2000)
# Size normalization
padded = pad_image(image, (4096, 4096), pad_value=0)
normalized = crop_or_pad(image, (512, 512))SDO/AIA and SDO/HMI data processing utilities.
from egghouse.sdo import aia_intscale, hmi_intscale, to_level15, Stacking
# AIA intensity scaling
scaled = aia_intscale(aia_data, wavelength=171)
# HMI magnetogram scaling
mag_scaled = hmi_intscale(hmi_data, vmin=-500, vmax=500)
# Level 1.0 to Level 1.5 conversion (north-up, centered, 4096x4096)
level15_map = to_level15('aia_lev1.fits') # auto-detect instrument
level15_map = to_level15('hmi_lev1.fits', instrument='HMI')
# Solar rotation-corrected stacking (with differential rotation)
stacker = Stacking(nb_stack=21, method='mean', latitude_deg=30)
mean_image = stacker.run(fits_files)
# Snodgrass differential rotation model
from egghouse.sdo import snodgrass_rotation_rate
rate_eq = snodgrass_rotation_rate(0) # 14.71 deg/day at equator
rate_60 = snodgrass_rotation_rate(60) # ~12.0 deg/day at 60° latitude
# DEM (Differential Emission Measure) analysis using SITES algorithm
from egghouse.sdo import (
dem_sites, dem_map, get_temperature_response, get_default_temperatures,
get_emission_measure, get_mean_temperature
)
# Setup temperature grid and response functions
temps = get_default_temperatures(logt_min=5.5, logt_max=7.5, n_bins=50)
response = get_temperature_response(temperatures=temps) # requires aiapy
# Single pixel DEM inversion
intensities = [10.0, 50.0, 200.0, 150.0, 80.0, 20.0] # 6 AIA channels (94-335 Å)
errors = [i * 0.1 for i in intensities]
dem, info = dem_sites(intensities, errors, response, temps)
# Full-map DEM with chunked processing
dem_cube, map_info = dem_map(image_cube, error_cube, response, temps)
# Derived quantities
em = get_emission_measure(dem, temps) # Total emission measure
t_mean = get_mean_temperature(dem, temps) # DEM-weighted mean temperatureML/DL configuration management with dataclass support.
from dataclasses import dataclass
from egghouse.config import BaseConfig
@dataclass
class TrainConfig(BaseConfig):
lr: float = 0.001
epochs: int = 100
batch_size: int = 32
# Load from various sources
config = TrainConfig.from_yaml('train_config.yaml')
config = TrainConfig.from_json('train_config.json')
config = TrainConfig.from_env(prefix="TRAIN_")
config = TrainConfig.from_args() # CLI: --config base.yaml --lr 0.0001
# Save
config.to_yaml('output_config.yaml')
config.to_json('output_config.json')PostgreSQL database utilities with simplified CRUD operations.
from egghouse.database import PostgresManager, load_config
config = load_config('database.yaml')
db = PostgresManager(**config)
# Insert
db.insert('observations', {'date': '2024-01-01', 'wavelength': 171})
# Upsert (insert or update on conflict)
db.upsert('observations',
{'filepath': '/data/aia.fits', 'wavelength': 171, 'processed': True},
conflict_columns='filepath',
update_columns=['processed'])
# Query
results = db.select('observations', where={'wavelength': 171})
df = db.to_dataframe(results, columns=['id', 'date', 'wavelength'])File transfer utilities with HTTP, FTP, and SFTP support.
from egghouse.transfer import download_single_file, download_parallel, get_file_list
# HTTP: Single file download
download_single_file('http://example.com/data.fits', 'data.fits')
# HTTP: Get file list from directory listing
files = get_file_list('http://example.com/data/', extensions=['fits'])
# HTTP: Parallel download
tasks = [('http://example.com/f1.fits', 'f1.fits'), ('http://example.com/f2.fits', 'f2.fits')]
download_parallel(tasks, parallel=4)
# FTP: Download files (no external dependencies)
from egghouse.transfer import ftp_connection, ftp_download_file, ftp_list_files
with ftp_connection('ftp.example.com', user='anonymous') as ftp:
files = ftp_list_files(ftp, '/data/', extensions=['fits'])
ftp_download_file(ftp, '/data/file.fits', 'local_file.fits')
# FTP: Parallel download
from egghouse.transfer import ftp_download_parallel
tasks = [('/remote/f1.fits', 'f1.fits'), ('/remote/f2.fits', 'f2.fits')]
result = ftp_download_parallel('ftp.example.com', tasks, parallel=4)
# SFTP: Secure file transfer (requires paramiko)
from egghouse.transfer import sftp_connection, sftp_download_file, sftp_upload_file
with sftp_connection('sftp.example.com', user='admin', key_file='~/.ssh/id_rsa') as sftp:
sftp_download_file(sftp, '/data/file.fits', 'local_file.fits')
sftp_upload_file(sftp, 'local_file.fits', '/upload/file.fits')export DB_HOST=localhost
export DB_PORT=5432
export DB_NAME=solar_data
export DB_USER=username
export DB_PASSWORD=password
export DB_LOG_QUERIES=true # Optional: enable query logginghost: localhost
port: 5432
database: solar_data
user: username
password: passwordDetailed Guides (in docs/ folder):
- io_guide.md - FITS, BMP file I/O
- image_guide.md - Image processing utilities
- sdo_guide.md - SDO/AIA/HMI data processing
- config_guide.md - Configuration management
- database_guide.md - PostgreSQL utilities
- transfer_guide.md - File transfer utilities (HTTP, FTP, SFTP)
API Reference: See API_REFERENCE.md for complete function signatures.
MIT License
이 저장소의 콘텐츠는 부분적으로 AI(Claude)의 도움을 받아 작성되었습니다. Some content in this repository was created with the assistance of AI (Claude).
- 일부 학습 자료는 AI가 생성하고 저자가 검토함 Some study materials were generated by AI and reviewed by the author
- 일부는 저자가 작성하고 AI가 검토함 Some were written by the author and reviewed by AI
Eunsu Park