Source code for brutus.priors.astrometric

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Astrometric priors for Bayesian stellar parameter estimation.

This module provides log-prior functions for astrometric measurements
including parallax priors and coordinate transformations. These priors
are essential for incorporating Gaia astrometry into stellar parameter
estimation.

Functions
---------
logp_parallax : Parallax prior
    Gaussian prior from measured parallax
logp_parallax_scale : Scale factor prior
    Prior on distance scale (s = 1/d^2)
convert_parallax_to_scale : Coordinate transform
    Convert parallax to flux scale factor

See Also
--------
brutus.priors.galactic : Galactic structure distance priors
brutus.analysis.individual.BruteForce : Uses parallax priors for fitting

Notes
-----
These priors incorporate astrometric information from missions like Gaia
to constrain stellar distances and luminosities.

The parallax prior is straightforward Gaussian, but care must be taken
with the coordinate transformation when using scale factors (s = 1/d^2)
rather than distances directly. The Jacobian must be properly accounted for.

Examples
--------
>>> from brutus.priors.astrometric import logp_parallax
>>> import numpy as np
>>>
>>> # Gaia parallax measurement
>>> p_meas = 2.5  # mas
>>> p_err = 0.1   # mas
>>>
>>> # Evaluate prior for model parallaxes
>>> parallaxes = np.linspace(1.0, 4.0, 100)
>>> log_prior = logp_parallax(parallaxes, p_meas, p_err)
"""

import numpy as np

__all__ = ["logp_parallax", "logp_parallax_scale", "convert_parallax_to_scale"]

# Numerical constants for uninformative prior bounds
SCALE_MIN = 1e-20  # Minimum scale factor (essentially zero)
SCALE_MAX = 1e20  # Maximum scale factor (essentially infinity)


[docs] def logp_parallax(parallaxes, p_meas, p_err): r""" Log-prior for parallax measurements assuming Gaussian errors. Implements a Gaussian log-prior based on observed parallax and measurement uncertainty. Returns uniform prior when measurements are invalid or unavailable. Parameters ---------- parallaxes : array_like Model parallax values in milliarcseconds (mas). p_meas : float Measured parallax in milliarcseconds (mas). p_err : float Parallax measurement uncertainty in milliarcseconds (mas). Returns ------- logp : array_like Log-prior probability density for the input parallax values. Returns 0 (uniform prior) if measurements are invalid. Notes ----- The log-prior follows a normal distribution: .. math:: \\log p(\\pi | \\pi_{\\text{obs}}, \\sigma_\\pi) = -\\frac{1}{2} \\left[ \\frac{(\\pi - \\pi_{\\text{obs}})^2}{\\sigma_\\pi^2} + \\log(2\\pi\\sigma_\\pi^2) \\right] For invalid measurements (non-finite values), returns uniform prior. """ parallaxes = np.asarray(parallaxes) # Check for valid measurements if np.isfinite(p_meas) and np.isfinite(p_err) and p_err > 0: # Gaussian log-prior chi2 = (parallaxes - p_meas) ** 2 / p_err**2 log_norm = np.log(2.0 * np.pi * p_err**2) logp = -0.5 * (chi2 + log_norm) else: # Uniform prior for invalid measurements logp = np.zeros_like(parallaxes, dtype=float) return logp
[docs] def logp_parallax_scale(scales, scale_errs, p_meas, p_err, snr_lim=4.0): r""" Log-prior for flux scale factors derived from parallax measurements. Applies parallax constraints to flux density scale factors where scale ~ parallax². Uses Gaussian approximation for high signal-to-noise parallax measurements. Parameters ---------- scales : array_like Flux density scale factors (proportional to parallax²). scale_errs : array_like Scale factor measurement uncertainties. p_meas : float Measured parallax in milliarcseconds (mas). p_err : float Parallax measurement uncertainty in milliarcseconds (mas). snr_lim : float, optional Minimum signal-to-noise ratio for applying Gaussian approximation. Below this threshold, returns uniform prior. Default is 4.0. Returns ------- logp : array_like Log-prior probability density for the input scale factors. Notes ----- For high SNR measurements (p_meas/p_err > snr_lim), the scale factor prior is derived from the parallax measurement using error propagation: .. math:: s = \\pi^2 + \\sigma_\\pi^2 \\sigma_s = \\sqrt{2\\sigma_\\pi^4 + 4\\pi^2\\sigma_\\pi^2} The total uncertainty combines model and measurement errors in quadrature. """ scales = np.asarray(scales) scale_errs = np.asarray(scale_errs) # Check SNR threshold and measurement validity if ( np.isfinite(p_meas) and np.isfinite(p_err) and p_err > 0 and abs(p_meas / p_err) > snr_lim ): # Convert parallax to scale factor statistics s_mean, s_std = convert_parallax_to_scale(p_meas, p_err, snr_lim) # Total variance (model + measurement) total_var = s_std**2 + scale_errs**2 # Gaussian log-prior chi2 = (scales - s_mean) ** 2 / total_var log_norm = np.log(2.0 * np.pi * total_var) logp = -0.5 * (chi2 + log_norm) else: # Uniform prior for low SNR or invalid measurements logp = np.zeros_like(scales, dtype=float) return logp
[docs] def convert_parallax_to_scale(p_meas, p_err, snr_lim=4.0): r""" Convert parallax measurement to flux density scale factor statistics. Transforms parallax measurements and uncertainties to scale factor (s ~ π²) mean and standard deviation using error propagation. Parameters ---------- p_meas : float Measured parallax in milliarcseconds (mas). p_err : float Parallax measurement uncertainty in milliarcseconds (mas). snr_lim : float, optional Minimum signal-to-noise ratio for conversion. Below this threshold, returns uninformative scale factor statistics. Default is 4.0. Returns ------- s_mean : float Mean of the scale factor distribution. s_std : float Standard deviation of the scale factor distribution. Notes ----- For high SNR measurements, uses error propagation: .. math:: s_{\\text{mean}} = \\max(0, \\pi_{\\text{meas}})^2 + \\sigma_\\pi^2 s_{\\text{std}} = \\sqrt{2\\sigma_\\pi^4 + 4\\pi_{\\text{meas}}^2\\sigma_\\pi^2} The parallax is floored at zero to handle negative measurements. For low SNR, returns uninformative statistics (tiny mean, huge std). Examples -------- >>> s_mean, s_std = convert_parallax_to_scale(1.0, 0.1) # 10-sigma detection >>> print(f"Scale factor: {s_mean:.3f} ± {s_std:.3f}") """ if np.isfinite(p_meas) and np.isfinite(p_err) and abs(p_meas / p_err) > snr_lim: # Floor parallax at zero to handle negative measurements p_positive = max(0.0, p_meas) # Scale factor statistics via error propagation s_mean = p_positive**2 + p_err**2 s_std = np.sqrt(2 * p_err**4 + 4 * p_positive**2 * p_err**2) else: # Uninformative prior for low SNR measurements s_mean, s_std = SCALE_MIN, SCALE_MAX return s_mean, s_std