SpectraRust/CLAUDE.md
2026-03-22 00:30:20 +08:00

128 lines
4.6 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Fortran stellar atmosphere modeling suite being refactored to Rust. Strategy: **split Fortran into modules first, then incrementally rewrite in Rust**.
- **TLUSTY 208**: Non-LTE stellar atmosphere calculator (~50,000 lines → 304 modules)
- **SYNSPEC 54**: Synthetic spectrum evaluator (~24,000 lines → 168 modules)
- **Progress**: 120/~472 Fortran units translated to Rust
## Environment Variables
```bash
export TL208=/home/fmq/program/tlusty
export TLUSTY=$TL208/tl208-s54
export LINELIST=$TL208/linelist
export IRON=$TL208/irondata
export OPTABLES=$TL208/optables
```
## Build Commands
### Rust
```bash
cargo build # Debug build
cargo build --release # Release build
cargo test # Run all tests (includes Fortran comparison tests)
cargo test test_expo # Run specific test
```
### Fortran
```bash
# Production (single file)
gfortran -O3 -fno-automatic -mcmodel=large -o tlusty/tlusty.exe tlusty/tlusty208.f
gfortran -O3 -fno-automatic -mcmodel=large -o synspec/synspec.exe synspec/synspec54.f
# Development (modular)
cd rust/tlusty/extracted && make # Output: build/tlusty_extracted
```
**Fortran compile flags:**
- `-mcmodel=large`: Required for large COMMON blocks (>2GB address space)
- `-fno-automatic`: Static storage (old Fortran compatibility)
- **Never use** `-ffixed-line-length-none`: Breaks columns 73-80 handling
## Rust Architecture
```
src/
├── lib.rs # Module exports
├── data.rs # Static data arrays (translated from BLOCK DATA)
├── math/ # Pure math functions (no COMMON dependency) - 120 modules
│ ├── expint.rs # Exponential integrals
│ ├── voigt.rs # Voigt profile
│ ├── tridag.rs # Tridiagonal solver
│ └── ...
├── state/ # COMMON block translations as structs
│ ├── constants.rs # Physical/math constants, array dimensions
│ ├── config.rs # Runtime config
│ ├── atomic.rs # Atomic/ion/level data
│ ├── model.rs # Atmosphere model state (largest struct)
│ ├── arrays.rs # Main linear equation arrays
│ ├── iterat.rs # Iteration control
│ ├── alipar.rs # ALI (Accelerated Lambda Iteration) arrays
│ └── odfpar.rs # ODF (Opacity Distribution Function) data
└── physics/ # Physics calculations (placeholder)
```
## Running Tests
### Rust Tests
```bash
cargo test # All unit tests
cargo test --test fortran_comparison # Fortran comparison tests only
```
### Fortran Integration Tests
```bash
# TLUSTY: H-He model test
cd $TLUSTY/tests/tlusty/hhe
$TLUSTY/tlusty/tlusty.exe < hhe35lt.5 > hhe35lt.6
cp fort.7 hhe35lt.7
diff hhe35lt.7 hhe35lt.7.bak
# SYNSPEC: spectrum test
cd $TLUSTY/tests/synspec/hhe
ln -sf $TLUSTY/data data
cp hhe35nl.7 fort.8
ln -sf fort.55.con fort.55
$TLUSTY/synspec/synspec.exe < hhe35nl.5
```
## Refactoring Workflow
1. **Find pure functions**: Check `rust/tlusty/extracted/_PURE_UNITS.txt` for units without COMMON dependencies
2. **Translate**: Create `src/math/<name>.rs`, add to `src/math/mod.rs`
3. **Verify**: Add test case in `tests/fortran_comparison.rs` with Fortran reference values
## Key Architecture
**TLUSTY COMMON blocks** (mapped to `src/state/` structs):
- `BASICS.FOR``constants.rs`: Array dimensions (`MDEPTH`=100, `MFREQ`=135000, `MLEVEL`=1134)
- `ATOMIC.FOR``atomic.rs`: Atomic masses, abundances, energy levels
- `MODELQ.FOR``model.rs`: Temperature, density, populations
- `ARRAY1.FOR``arrays.rs`: Main linear equation arrays
**SYNSPEC** reads model atmosphere from `fort.8`, outputs spectrum to `fort.7`
## Fortran → Rust Translation Notes
Critical patterns to avoid mistakes:
- **Index conversion**: Fortran 1-indexed → Rust 0-indexed. `arr(i)``arr[i-1]`, `DO I=1,N``for i in 0..n`
- **Expression precedence**: `-LOG(X)` is `-(LOG(X))`, not `LOG(-X)`
- **Decrementing loops**: Use `isize`/`i32` when loop variable may become negative
- **COMMON dependency**: Check INCLUDE statements before assuming a function is pure
- **Large structs**: Functions with many COMMON dependencies use lifetime structs:
```rust
pub struct Params<'a> {
pub input: &'a [f64],
pub output: &'a mut [f64],
}
```
- **Type inference**: `(z1 - z2).powi(2)` may fail; use `(z1 - z2) * (z1 - z2)`
- **Test tolerance**: Polynomial approximations (Abramowitz-Stegun) need relaxed tolerance (~1e-7)