Source code for jetset.internal_absorption

"""Internal absorption modeling utilities for jet spectral components."""

import os
import ctypes

on_rtd = os.environ.get('READTHEDOCS', None) == 'True'

if on_rtd:
    from .mock import jetkernel as BlazarSED
else:
    from .jetkernel import jetkernel as BlazarSED


import numpy as np


def _get_c_array_read_only(ptr, size):
    size = int(size)
    if size <= 0 or int(ptr) == 0:
        return np.zeros(0, dtype=np.float64)
    arr = (ctypes.c_double * size).from_address(int(ptr))
    return np.ctypeslib.as_array(arr).copy()


[docs] class InternalAbsorption(object): """Compute internal gamma-gamma absorption from jet seed photon fields. Notes ----- Evaluates optical depth ``tau(nu)`` using BLR, DT, or Corona photon distributions through the C backend and provides attenuation factors for integration into jet spectral component calculations. """ def __init__(self, jet, nu_min=None, seed_photons_name='BLR', N_soft=50, N_hard=50, N_R_H=20, N_theta=20, use_R_H_profile_extrapolation=False, use_sigma_gamma_gamma_fast=False, ): """Create a new `InternalAbsorption` instance. Parameters ---------- jet : object Jet model instance. nu_min : object, optional Minimum frequency in Hz. seed_photons_name : str, optional Seed-photon field identifier (for example ``BLR``, ``DT``, or ``Corona``). N_soft : int, optional Number of soft-photon energy samples. N_hard : int, optional Number of hard-photon energy samples. N_R_H : int, optional Number of distance samples along ``R_H``. N_theta : int, optional Number of angular samples. use_R_H_profile_extrapolation : bool, optional If ``True``, enable r h profile extrapolation. use_sigma_gamma_gamma_fast : bool, optional If ``True``, enable tabulated/interpolated sigma_gamma_gamma in C backend. """ if seed_photons_name in ['BLR','DT','Corona']: self._seed_photons_name=seed_photons_name else: raise RuntimeError('seed_photons_name %s not valid'%seed_photons_name) self._N_soft=N_soft self._N_hard=N_hard self._N_R_H=N_R_H self._N_theta=N_theta self._nu_min=nu_min self._jet=jet self._use_R_H_profile_extrapolation=use_R_H_profile_extrapolation self._use_sigma_gamma_gamma_fast=bool(use_sigma_gamma_gamma_fast)
[docs] def set_use_sigma_gamma_gamma_fast(self, use_fast): self._use_sigma_gamma_gamma_fast = bool(use_fast)
[docs] def eval_tau_photons(self, nu_src, R_H, peak=False, use_R_H_profile_extrapolation=False): """Evaluate tau photons. Parameters ---------- nu_src : object Source-frame frequency array in Hz. R_H : object Distance from black hole in cm. peak : bool, optional If ``True``, use peak-optimized seed-photon sampling. use_R_H_profile_extrapolation : bool, optional If ``True``, enable r h profile extrapolation. Notes ----- Internal absorption is evaluated via ``eval_internal_abs_tau_isolated`` only. Returns ------- object Computed value. """ nu_src = np.atleast_1d(np.asarray(nu_src, dtype=np.float64)) if nu_src.size == 0: return np.zeros(0, dtype=np.float64), np.zeros(0, dtype=np.float64) nu_src_max = float(np.max(nu_src)) r_h_override = -1.0 if R_H is None else float(R_H) nu_min = -1.0 if self._nu_min is None else float(self._nu_min) if not hasattr(BlazarSED, 'eval_internal_abs_tau_isolated'): raise RuntimeError('jetkernel extension is missing eval_internal_abs_tau_isolated; rebuild the C extension.') if self._seed_photons_name == "BLR": comp_cfg = self._jet._blob.core.internal_abs.BLR elif self._seed_photons_name == "DT": comp_cfg = self._jet._blob.core.internal_abs.DT else: comp_cfg = self._jet._blob.core.internal_abs.Corona comp_cfg.use_sigma_gamma_gamma_fast = int(self._use_sigma_gamma_gamma_fast) tau_size = BlazarSED.eval_internal_abs_tau_isolated( self._jet._blob, self._seed_photons_name, nu_min, int(self._N_soft), int(self._N_hard), int(self._N_R_H), int(self._N_theta), int(bool(use_R_H_profile_extrapolation)), int(bool(peak)), nu_src_max, r_h_override, ) if tau_size < 0: raise RuntimeError('internal absorption C evaluation failed for component %s' % self._seed_photons_name) if self._seed_photons_name == "BLR": comp = self._jet._blob.core.internal_abs.BLR elif self._seed_photons_name == "DT": comp = self._jet._blob.core.internal_abs.DT else: comp = self._jet._blob.core.internal_abs.Corona nu_tau = _get_c_array_read_only(comp.nu_tau, comp.tau_size) tau = _get_c_array_read_only(comp.tau, comp.tau_size) return tau,nu_tau
def _get_default_nu_src(self): sed_nu_src = None if hasattr(self._jet, 'spectral_components'): try: sed_nu_src = self._jet.spectral_components.Sum.SED.nu_src except Exception: sed_nu_src = None if sed_nu_src is not None: try: sed_nu_src = np.atleast_1d(np.asarray(sed_nu_src.value, dtype=np.float64)) except Exception: sed_nu_src = None if sed_nu_src is not None and sed_nu_src.size > 0 and np.all(np.isfinite(sed_nu_src)): return sed_nu_src nu_src, _ = self._jet._prepare_nu_model(nu=None, loglog=False) nu_src = np.atleast_1d(np.asarray(nu_src, dtype=np.float64)) if nu_src.size == 0 or np.any(~np.isfinite(nu_src)): raise RuntimeError('invalid jet frequency grid for internal absorption evaluation') return nu_src
[docs] def eval(self, get_tau=False, lin_nu=None, peak=False): """Evaluate model output. Parameters ---------- get_tau : bool, optional If ``True``, return optical depth instead of attenuation. lin_nu : object, optional Linear-frequency array in Hz. peak : bool, optional If ``True``, use peak-optimized seed-photon sampling. Returns ------- object Computed value. """ if lin_nu is None: nu_src = self._get_default_nu_src() else: nu_src = np.atleast_1d(np.asarray(lin_nu, dtype=np.float64)) tau,nu_tau=self.eval_tau_photons(nu_src, R_H=self._jet.parameters.R_H.val, peak=peak, use_R_H_profile_extrapolation=self._use_R_H_profile_extrapolation) EPS = 1e-300 nu_src_pos = np.maximum(nu_src, EPS) nu_tau_pos = np.maximum(nu_tau, EPS) tau_pos = np.maximum(tau, EPS) tau_interp = 10**np.interp( np.log10(nu_src_pos), np.log10(nu_tau_pos), np.log10(tau_pos), left=np.log10(EPS), right=None ) if get_tau: return tau_interp,nu_src_pos return np.exp(-tau_interp),nu_src_pos