Skip to content

Conversation

@nikhil-sarin
Copy link
Owner

No description provided.

Implements comprehensive infrastructure for joint analysis of transients
across multiple messengers (optical, X-ray, radio, GW, neutrinos).

New features:
- MultiMessengerTransient class for managing multi-messenger data
- fit_joint() method for joint parameter estimation with shared parameters
- fit_individual() method for comparison with independent fits
- Support for external likelihoods (GW, neutrino)
- Utility function create_joint_prior() for building joint priors
- Dynamic add/remove messenger capability

Added comprehensive documentation:
- docs/multimessenger.txt: Full user guide with examples
- examples/multimessenger_example.py: Complete worked example
- test/multimessenger_test.py: Unit tests for all functionality

Integration:
- Updated redback/__init__.py to export new module
- Updated docs/index.rst to include multimessenger documentation
- Fully compatible with existing redback and bilby infrastructure

This addresses the need for joint GW+EM analysis and extends
the existing joint_grb_gw_example.py with a more general framework.
Extends multi-messenger framework with comprehensive support for jointly
fitting spectroscopic and photometric data from the same transient.

New content:
- examples/joint_spectrum_photometry_example.py: Complete worked example
  * Demonstrates joint fitting of multi-band photometry + spectrum
  * Shows how to use custom likelihoods for different data types
  * Includes handling of multiple spectra at different epochs
  * Provides comparison between individual and joint fits

Documentation updates (docs/multimessenger.txt):
- New "Joint Spectrum and Photometry Analysis" section
  * Basic approach using custom likelihoods
  * Setting up priors for shared/epoch-specific parameters
  * Running joint analysis workflow
  * Handling multiple spectra at different epochs
  * Best practices for time synchronization, parameter consistency
  * Comparison with individual fits
- Updated Examples section to reference new example
- Updated References section

This addresses a common use case in supernova/kilonova studies where
observers have both time-series photometry and spectroscopy at specific
epochs, enabling better constraints on physical parameters.
@nikhil-sarin nikhil-sarin added the enhancement New feature or request label Nov 8, 2025
@nikhil-sarin nikhil-sarin changed the title [WIP] - A new multi-messenger class structure for joint likelihood analyses e.g., GRB + EM + Neutrino or Spectra + photometry [WIP] - A new multi-messenger class structure for joint likelihood analyses e.g., GW + EM + Neutrino or Spectra + photometry Nov 10, 2025
claude and others added 23 commits November 12, 2025 12:10
Extends multi-messenger framework with support for simultaneously
fitting host galaxy and transient components in spectroscopy.
This addresses the critical issue of host galaxy contamination in
transient spectroscopy.

New content:
- examples/joint_galaxy_transient_spectrum_example.py (400+ lines)
  * Demonstrates combined model approach (observed = galaxy + transient)
  * Shows parameter biases when galaxy is ignored
  * Includes comparison of biased vs. unbiased fits
  * Covers advanced topics: emission lines, systematic checks
  * Complete workflow with best practices

Documentation updates (docs/multimessenger.txt):
- New "Joint Galaxy and Transient Spectrum Analysis" section
  * Why host contamination matters (bias in parameters)
  * Combined model approach with code examples
  * Setting up priors for galaxy and transient parameters
  * Comparison with transient-only fits
  * Adding galaxy emission lines (H-alpha, H-beta, [OIII])
  * Best practices: pre-explosion data, model selection
  * Common applications: SNe, TDEs, kilonovae, AGN
- Updated Examples section to reference new example

Key use cases:
- Supernova spectroscopy (especially SNe Ia in bright galaxies)
- Tidal disruption events (TDEs in galaxy centers)
- Any transient with significant host contamination
- Separating transient from AGN or stellar contributions

This complements the existing spectrum+photometry joint fitting,
providing comprehensive spectroscopic analysis capabilities.
…fitting

Extends test coverage for multi-messenger framework with two new test classes:

1. JointGalaxyTransientSpectrumTest (14 tests)
   - Combined model creation and output validation
   - Likelihood creation with galaxy+transient models
   - Likelihood evaluation with correct parameters
   - Comparison showing transient-only model gives biased results
   - Prior setup for joint galaxy+transient parameters
   - Integration with MultiMessengerTransient custom likelihoods
   - Gaussian emission line modeling (H-alpha, H-beta, etc.)
   - Galaxy model with emission lines
   - Shared redshift constraint validation
   - Component decomposition extraction
   - Flux ratio calculations
   - Multiple spectra at different epochs with evolving transient

2. SpectrumPhotometryJointFittingTest (4 tests)
   - Verifies spectrum and photometry are different data types
   - Creating separate likelihoods for different data types
   - Combining likelihoods using bilby.JointLikelihood
   - MultiMessengerTransient integration with custom likelihoods

Tests validate:
- Model correctness and parameter inference
- Likelihood computation and evaluation
- Prior sampling and constraints
- Component separation and flux ratios
- Time evolution of transient vs. constant galaxy
- Integration with bilby infrastructure

All tests use synthetic data to ensure reproducibility and fast execution.
The test was failing because the wavelength array doesn't have an exact
point at the emission line center, causing the peak amplitude to be less
than the input amplitude. Changed assertion from exact match (20 decimal
places) to a range check: peak must be >50% of input (reasonable for any
grid spacing) and <=100% (Gaussian peak is at center).
Adds 30+ new tests to significantly increase code coverage of the
multimessenger.py module. New test classes:

1. MultiMessengerCoreFunctionalityTest (24 tests)
   - _build_likelihood_for_messenger() with callable/string models
   - Invalid model names and unsupported likelihood types (error paths)
   - Different likelihood types (GaussianLikelihoodQuadratureNoise)
   - fit_joint() with single/multiple likelihoods (mocked sampler)
   - fit_joint() with external likelihoods (GW, neutrino)
   - fit_joint() with dict priors (not PriorDict)
   - fit_joint() error handling (no likelihoods)
   - fit_joint() default outdir and label
   - fit_joint() metadata verification
   - fit_individual() with multiple messengers
   - fit_individual() missing model/prior handling
   - fit_individual() default outdir
   - UV and infrared transient support
   - Neutrino likelihood support
   - Removing nonexistent messengers

2. CreateJointPriorAdvancedTest (3 tests)
   - Multiple shared parameters
   - Empty individual priors
   - Shared params not in any messenger

Key improvements:
- Uses mocking to test fit methods without actual sampling
- Tests error handling and edge cases
- Covers all likelihood types
- Tests metadata construction
- Tests default parameter handling
- Covers UV, infrared, and neutrino messengers

This should bring code coverage for multimessenger.py to >90%.
Adds EdgeCasesAndWarningsTest class with 25 new tests covering:
- Time error handling in likelihood building
- Warning paths for missing models/priors
- Single likelihood warnings
- Shared parameter logging
- Callable model metadata
- Directory creation
- All logger info/warning calls
Adds RealCodePathsTest class with 10 new tests using actual Transient
objects instead of mocks to ensure real code execution:
- Custom likelihoods dict update path
- String model name lookup in model library
- None entries filtering in messengers dict
- JointLikelihood construction verification
- All sampler parameter passing
- Full EM messenger type initialization (optical, xray, radio, uv, infrared)

Total: 90 tests, 2135 lines
Include the new multimessenger test file in the CI test group 4
so that coverage is reported to Coveralls.
Use sigma_i instead of sigma for GaussianLikelihoodQuadratureNoise
as per its __init__ signature.
The coverage was showing zero because the workflow was trying to combine
XML reports instead of .coverage database files. This commit fixes the issue by:

1. Saving .coverage database files from each test group with unique names
2. Downloading all .coverage.* files in the coverage job
3. Using 'coverage combine' to properly merge the database files
4. Generating a combined XML report from the merged data
5. Uploading the combined report to Coveralls

This ensures that coverage from all 4 test groups is properly combined
and reported to Coveralls.
Changed the approach to download artifacts into a dedicated directory
instead of using merge-multiple to avoid coverage.xml files overwriting
each other. Now we:

1. Download all artifacts to coverage-artifacts/ directory
2. Find all .coverage.* files recursively
3. Copy them to the working directory
4. Combine them properly

Added debug output to help diagnose any remaining issues.
Added detailed debugging to identify why .coverage files aren't being found:

Test stage:
- List all files after pytest to see what's created
- Add error checking if .coverage file doesn't exist
- Fail fast with clear error message

Coverage stage:
- Show contents of each artifact directory
- List all files recursively in artifacts
- Better error handling

Upload stage:
- Added if-no-files-found: error to catch upload failures early

This will help diagnose whether the issue is with:
1. .coverage file creation by pytest
2. File upload to artifacts
3. File download from artifacts
The issue was that .coverage.* files weren't being uploaded when
specified in a multi-line path. Fixed by:

1. Splitting upload into two separate steps:
   - coverage-xml-N for XML reports
   - coverage-db-N for .coverage database files

2. Each upload has if-no-files-found: error for immediate failure
   detection

3. Download only coverage-db-* artifacts (we don't need XMLs)

4. Added verbose flag (-v) to cp command for better debugging

This ensures .coverage files are properly uploaded and downloaded
for combining into the final coverage report.
The issue was that pytest-cov wasn't creating the .coverage database
file. Fixed by:

1. Using --cov-report= (empty) to force database creation without
   generating any report during pytest
2. Running 'coverage xml' separately to generate the XML report from
   the .coverage database
3. Added explicit check to fail early if .coverage file doesn't exist

This ensures the .coverage database file is always created, which is
required for combining coverage across test groups.
After reviewing coverage.py documentation, the correct approach is:

1. Create .coveragerc with parallel=True - This makes coverage automatically
   create .coverage.* files with unique suffixes (hostname.pid.random)

2. Run pytest-cov normally - With parallel=True in config, it automatically
   creates .coverage.* files instead of a single .coverage file

3. Upload .coverage.* files - No manual renaming needed

4. Run 'coverage combine' without arguments - It automatically finds and
   combines all .coverage.* files in the current directory

Changes:
- Added .coveragerc with parallel=True configuration
- Removed manual file renaming in test jobs
- Simplified artifact upload to just .coverage.* files
- Fixed coverage combine to run without arguments as documented

Reference: https://coverage.readthedocs.io/en/latest/cmd.html#combining-data-files-coverage-combine
The .coverage.* files weren't being uploaded because they start with
a dot, making them hidden files. The upload-artifact@v4 action has
include-hidden-files: false by default.

Added include-hidden-files: true to the upload step to ensure
.coverage.* files are included in the artifact.
Key insight from pytest-cov docs: pytest-cov overrides the parallel
option, so .coveragerc with parallel=True doesn't work as expected.

Instead:
1. Each test job runs pytest-cov which creates a plain .coverage file
2. Upload each .coverage file as a separate artifact
3. Download artifacts (each in its own directory)
4. Copy and rename .coverage files to .coverage.1, .coverage.2, etc.
5. Run coverage combine to merge them

This follows how pytest-cov actually works rather than trying to force
parallel mode which pytest-cov doesn't use.
This test file was being run in the original workflow but was missing
from the parallel test groups, causing the 5% coverage drop from 88% to 83%.

Added test/test_on_ref_results.py to Group 4 to restore full coverage.
The old workflow didn't have this file and achieved 88% coverage.
pytest-cov overrides the parallel option anyway, making this file
unnecessary and potentially causing the coverage discrepancy.
…er-analysis-framework-011CUuaGfKWyGEAP4Dxc1e9k
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants