Source code for ForMoSA.transform.spectroscopic_effects

from ForMoSA.core.errors import ForMoSAError
import ForMoSA.utils.logL_functions as logL_functions
from ForMoSA.observation.observation_base import Observation
from ForMoSA.transform.observed import ObservedModel, ObservedParameters
from ForMoSA.core.enums import ParameterKind, ObservationType, LogLikelihoodType
from ForMoSA.transform.apply_effects import ApplyPhysicsEffects, ApplyObservationEffects

[docs] class SpectralEffects: ''' Physical effects applied to a spectroscopic model. Notes ----- Authors: Allan Denis ''' @staticmethod def _apply_physics(observed_model: ObservedModel, observed_params: ObservedParameters) -> ObservedModel: ''' Apply the physics effects relevant to spectroscopy. Parameters ---------- observed_model : ObservedModel Instance of class ObservedModel params : ObservedParameters Instance of class ObservedParameters Returns ------- (ObservedModel): Instance of class ObservedModel transformed by the physics effects Notes ----- Authors: Allan Denis ''' # ====================== # Initial checkins # ====================== # Instance of observed_model if not isinstance(observed_model, ObservedModel): raise ForMoSAError(f' Wrong type for observed_model: {type(observed_model)}. Expected an ObservedModel') # Instance of observed_params if not isinstance(observed_params, ObservedParameters): raise ForMoSAError(f' Wrong type for observed_params: {type(observed_params)}. Expected an ObservedParameters') values_by_kind = observed_params.values_by_kind # ====================== # RV # ====================== if observed_params.has_kind(ParameterKind.RV): observed_model = ApplyPhysicsEffects._apply_rv(observed_model, values_by_kind[ParameterKind.RV]) # ====================== # v.sini # ====================== if ParameterKind.VSINI in values_by_kind: vsini_param = observed_params.params_by_kind[ParameterKind.VSINI] observed_model = ApplyPhysicsEffects._apply_vsini(observed_model, values_by_kind[ParameterKind.VSINI], values_by_kind[ParameterKind.LD], vsini_param.vsini_function) # ====================== # Reddening # ====================== if ParameterKind.AV in values_by_kind: observed_model = ApplyPhysicsEffects._apply_reddening(observed_model, values_by_kind[ParameterKind.AV]) # ====================== # CPD # ====================== if ParameterKind.BB_T in values_by_kind: if ParameterKind.BB_R not in values_by_kind: raise ForMoSAError(' Black Body radius is required when a Black Body temperature is given') if ParameterKind.DISTANCE not in values_by_kind: raise ForMoSAError(' Distance is required to add a CPD contribution') observed_model = ApplyPhysicsEffects._apply_cpd(observed_model, values_by_kind[ParameterKind.BB_T], values_by_kind[ParameterKind.BB_R], values_by_kind[ParameterKind.DISTANCE]) # ====================== # Scaling (R, D, alpha) # ====================== if ParameterKind.DISTANCE in values_by_kind and ParameterKind.RADIUS in values_by_kind: observed_model = ApplyPhysicsEffects._apply_scaling(observed_model, values_by_kind.get(ParameterKind.RADIUS), values_by_kind.get(ParameterKind.DISTANCE)) alpha = values_by_kind.get(ParameterKind.ALPHA, 1.0) observed_model.flux *= alpha return observed_model @staticmethod def _apply_observation(observed_model: ObservedModel, obs: Observation, bounds: tuple[float, float] = (-float('inf'), float('inf'))) -> ObservedModel: ''' Apply the observational effects relevant to spectroscopy. Parameters ---------- observed_model : ObservedModel Instance of class ObservedModel obs : Observation Instance of class Observation bounds : tuple[float, float] Bounds for the least squares Returns ------- ObservedModel Instance of class ObservedModel transformed by the observational effects Notes ----- Authors: Allan Denis ''' # ====================== # Initial checkins # ====================== # Instance of observed_model if not isinstance(observed_model, ObservedModel): raise ForMoSAError(f' Wrong type for observed_model: {type(observed_model)}. Expected a ObservedModel') # Instance of observation if not isinstance(obs, Observation): raise ForMoSAError(f' Wrong obs type: {type(obs)}. Expected an Observation') # ObsType if obs.ObsType != ObservationType.SPECTROSCOPIC.value: raise ForMoSAError(f' Wrong observation type: {obs.ObsType}. Expected {ObservationType.SPECTROSCOPIC.value}') # ====================== # Resolution decreasing # ====================== if len(observed_model.wave) != len(obs.wave): observed_model = ApplyObservationEffects._apply_resolution_decreasing(observed_model, obs) # ====================== # Scaling # ====================== if observed_model.scaling == 'analytic' and not obs.hc_mode: observed_model = ApplyObservationEffects._apply_scaling(observed_model, obs) # ====================== # High-contrast modeling # ====================== if obs.hc_mode: observed_model = ApplyObservationEffects._hc_modeling(observed_model, obs, bounds=bounds) return observed_model @staticmethod def _compute_loglike(observed_model: ObservedModel, obs: Observation, logL_type: LogLikelihoodType) -> float: ''' Compute the loglikelihood given an ObservedModel, an Observation and a loglikelihood function. Parameters ---------- observed_model : ObservedModel Observed model obs : Observation Observation logL_type : LogLikelihoodType Loglikelihood function Returns ------- float logL value Notes ----- Authors: Simon Petrus, Matthieu Ravet and Allan Denis ''' # ====================== # Initial checks # ====================== if not isinstance(observed_model, ObservedModel): raise ForMoSAError(f' Wrong type for model: {type(observed_model)}. Require an ObservedModel') if not isinstance(obs, Observation): raise ForMoSAError(f' Wrong type for observation: {type(obs)}. Require an Observation') if not isinstance(logL_type, LogLikelihoodType): raise ForMoSAError(f' Wrong type for logL_type {type(logL_type)}. Require a LogLikelihoodType') if obs.ObsType is not ObservationType.SPECTROSCOPIC.obstype: raise ForMoSAError(f' Wrong Observation type: {obs.ObsType}. Use a spectroscopic observation') if len(observed_model.flux) != len(obs.flux): raise ForMoSAError(f' Wrong length for model: {len(observed_model.flux)}. Expected the same length as the observations: {len(obs.flux)}') # Residuals and residuals with the componant only residuals = observed_model.residuals(obs.flux) residuals_componant = observed_model.residuals(obs.flux, component_only=True) # ===================== # Compute loglikelihood # ===================== logL_dict = { LogLikelihoodType.CHI2: lambda: logL_functions.logL_chi2(residuals, obs.err), LogLikelihoodType.CHI2_COVARIANCE: lambda: logL_functions.logL_chi2_covariance(residuals, obs.cov, obs.inv_cov), LogLikelihoodType.CCF_BROGI: lambda: logL_functions.logL_CCF_Brogi(residuals_componant, observed_model.flux), LogLikelihoodType.CCF_ZUCKER: lambda: logL_functions.logL_CCF_Zucker(residuals_componant, observed_model.flux), LogLikelihoodType.CCF_CUSTOM: lambda: logL_functions.logL_CCF_custom(residuals_componant, observed_model.flux, obs.err), LogLikelihoodType.CHI2_NOISESCALING: lambda: logL_functions.logL_chi2_noisescaling(residuals, obs.err), LogLikelihoodType.CHI2_NOISESCALING_COVARIANCE: lambda: logL_functions.logL_chi2_noisescaling_covariance(residuals, obs.cov, obs.inv_cov) } return logL_dict.get(logL_type, lambda: 0)()