Fennel v2.0 Upgrade Guide¶
Overview¶
Fennel v2.0 introduces significant API improvements while maintaining 100% backward compatibility. All existing code will continue to work without modifications. This guide shows you how to take advantage of the new features.
What's New in v2.0¶
1. Result Container Classes¶
Instead of remembering tuple unpacking order, use structured result objects:
Old way (still works):
dcounts, angles = fennel.track_yields(100.0)
dcounts, dcounts_s, long_prof, angles = fennel.em_yields(1000.0, particle=11)
New way:
result = fennel.track_yields_v2(100.0)
print(result.dcounts)
print(result.angles)
print(result.energy) # Metadata included!
print(result) # Pretty print: TrackYieldResult(energy=100.0 GeV, ...)
em_result = fennel.em_yields_v2(1000.0, particle=11)
print(em_result.particle_name) # "electron"
print(em_result.dcounts)
print(em_result.longitudinal_profile)
2. Better Error Messages with Validation¶
Old behavior:
New behavior:
from fennel import ValidationError
try:
fennel.track_yields_v2(-100.0)
except ValidationError as e:
print(e)
# "Energy must be positive, got -100.0 GeV. Example: energy=100.0"
Helpful suggestions for common mistakes:
fennel.em_yields_v2(1000.0, particle=999)
# ValidationError: Unknown particle PDG ID: 999.
# Supported particles: [11, -11, 22, ...].
# Common examples:
# EM cascades: 11 (electron), -11 (positron), 22 (photon)
3. Convenience Methods¶
Quick calculations with minimal parameters:
# Old way
dcounts, angles = fennel.track_yields(
100.0,
wavelengths=config["advanced"]["wavelengths"],
angle_grid=config["advanced"]["angles"],
n=config["mediums"][config["scenario"]["medium"]]["refractive index"]
)
# New way
result = fennel.quick_track(100.0)
Universal calculate() method:
# Auto-detects particle type from PDG ID
result = fennel.calculate(100.0, particle=11) # EM cascade
result = fennel.calculate(100.0, particle=211) # Hadron cascade
result = fennel.calculate(100.0, particle=13) # Track
# Or use friendly names
result = fennel.calculate(100.0, particle_type='electron')
result = fennel.calculate(100.0, particle_type='pion')
result = fennel.calculate(100.0, particle_type='muon')
Quick cascade for any particle:
# Automatically routes to em_yields or hadron_yields
electron_result = fennel.quick_cascade(1000.0, particle=11)
pion_result = fennel.quick_cascade(1000.0, particle=211)
Migration Examples¶
Example 1: Track Analysis¶
Before (v1.x):
from fennel import Fennel
import numpy as np
fennel = Fennel()
wavelengths = np.linspace(300, 600, 100)
# Must remember tuple order
dcounts, angles = fennel.track_yields(100.0, wavelengths=wavelengths)
# Calculate total photons
total_photons = np.trapezoid(dcounts, wavelengths)
print(f"Total photons: {total_photons}")
fennel.close()
After (v2.0 - recommended):
from fennel import Fennel
import numpy as np
fennel = Fennel()
# Structured result with metadata
result = fennel.track_yields_v2(100.0)
# Self-documenting code
total_photons = np.trapezoid(result.dcounts, wavelengths)
print(f"Energy: {result.energy} GeV")
print(f"Interaction: {result.interaction}")
print(f"Total photons: {total_photons}")
print(result) # Pretty print
fennel.close()
Example 2: Cascade Comparison¶
Before (v1.x):
# EM cascade
em_dc, em_dcs, em_lp, em_ang = fennel.em_yields(1000.0, particle=11)
# Hadron cascade - different tuple size!
had_dc, had_dcs, had_em_frac, had_em_frac_s, had_lp, had_ang = \
fennel.hadron_yields(1000.0, particle=211)
print(f"Hadron EM fraction: {had_em_frac:.2%}")
After (v2.0 - recommended):
# Consistent interface
em_result = fennel.em_yields_v2(1000.0, particle=11)
hadron_result = fennel.hadron_yields_v2(1000.0, particle=211)
print(f"EM particle: {em_result.particle_name}")
print(f"Hadron particle: {hadron_result.particle_name}")
print(f"Hadron EM fraction: {hadron_result.em_fraction:.2%}")
# Or use convenience method
electron = fennel.quick_cascade(1000.0, particle=11)
pion = fennel.quick_cascade(1000.0, particle=211)
Example 3: Error Handling¶
Before (v1.x):
# Errors could be cryptic
try:
dcounts, angles = fennel.track_yields(-100.0)
except Exception as e:
print(f"Something went wrong: {e}")
# User has to debug what went wrong
After (v2.0 - recommended):
from fennel import ValidationError
try:
result = fennel.track_yields_v2(-100.0)
except ValidationError as e:
print(e)
# Clear message: "Energy must be positive, got -100.0 GeV. Example: energy=100.0"
# User knows exactly what to fix!
API Comparison Table¶
| Feature | Old API | New API (v2.0) | Status |
|---|---|---|---|
| Track yields | track_yields() |
track_yields_v2() |
Both work |
| EM cascade yields | em_yields() |
em_yields_v2() |
Both work |
| Hadron cascade yields | hadron_yields() |
hadron_yields_v2() |
Both work |
| Return type | Tuple | Result Container | v2 only |
| Input validation | Minimal | Comprehensive | v2 only |
| Error messages | Basic | Helpful with examples | v2 only |
| Quick methods | - | quick_track(), quick_cascade() |
v2 only |
| Universal method | auto_yields() |
calculate() |
Both work |
When to Upgrade¶
Use v2 API when:¶
- Starting a new project
- You want better error messages
- You prefer self-documenting code
- You're tired of remembering tuple order
- You want IDE autocomplete for result attributes
Keep old API when:¶
- You have working code you don't want to touch
- You're maintaining legacy code
- You prefer the terseness of tuple unpacking
Backward Compatibility Guarantee¶
All v1.x code works in v2.0 without any changes:
# This still works exactly as before
from fennel import Fennel, config
config["general"]["random state seed"] = 42
fennel = Fennel()
# Old API - still supported
dcounts, angles = fennel.track_yields(100.0)
dcounts, dcounts_s, long_prof, angles = fennel.em_yields(1000.0, particle=11)
dcounts, dcounts_s, em_frac, em_frac_s, long_prof, angles = \
fennel.hadron_yields(1000.0, particle=211)
fennel.close()
New Exports¶
from fennel import (
Fennel, # Main class (same as before)
config, # Configuration (same as before)
TrackYieldResult, # New: Result container for tracks
EMYieldResult, # New: Result container for EM cascades
HadronYieldResult, # New: Result container for hadron cascades
ValidationError # New: Clear error messages
)
Testing¶
All 40 physics regression tests pass - calculations are identical to v1.x. 20 new tests verify v2 API features and backward compatibility.
# Run all tests
pytest tests/
# Run regression tests (verify physics unchanged)
pytest tests/test_physics_regression.py
# Run v2 API tests
pytest tests/test_v2_api.py
Performance¶
No performance impact - the v2 API is a thin wrapper around the existing implementation. The old and new APIs produce identical results.
Questions?¶
- Q: Do I need to upgrade my code?
-
A: No! All existing code works without changes.
-
Q: What if I find the new API too verbose?
-
A: Keep using the old API! It's fully supported.
-
Q: Can I mix old and new APIs?
-
A: Absolutely! Use whichever is most convenient for each situation.
-
Q: Will the old API be deprecated?
- A: No plans to deprecate. Both APIs are first-class citizens.
Summary¶
Fennel v2.0 offers significant quality-of-life improvements while respecting your existing codebase. Adopt the new features at your own pace - there's no pressure to change working code. The choice is yours!