Source code for species.util.box_util
"""
Utility functions for boxes.
"""
import warnings
from numbers import Real
import numpy as np
from beartype import beartype
from beartype.typing import Dict, Optional
from species.core.box import ObjectBox
from species.read.read_model import ReadModel
from species.util.core_util import print_section
[docs]
@beartype
def update_objectbox(
objectbox: ObjectBox, model_param: Dict[str, Real], model: Optional[str] = None
) -> ObjectBox:
"""
Function for updating the spectra and/or photometric fluxes in
an :class:`~species.core.box_types.ObjectBox`, for example to
apply a flux scaling and/or error inflation.
Parameters
----------
objectbox : species.core.box.ObjectBox
Box with the object's data, including the spectra
and/or photometric fluxes.
model_param : dict
Dictionary with the model parameters. Should contain the
value(s) of the flux scaling and/or the error inflation.
model : str, None
Name of the atmospheric model. Not required when
``model='petitradtrans'`` because the error inflation is
implemented differently with
:class:`~species.fit.retrieval.AtmosphericRetrieval`.
Returns
-------
species.core.box.ObjectBox
The input box which includes the spectra with the
scaled fluxes and/or inflated errors.
"""
print_section("Update ObjectBox")
if objectbox.flux is not None:
for phot_key, phot_value in objectbox.flux.items():
instr_name = phot_key.split(".")[0]
if (
f"error_{phot_key}" in model_param
or f"log_error_{phot_key}" in model_param
):
# Inflate the photometric uncertainty of a filter
if model is None:
warnings.warn(
"The dictionary with model parameters "
"contains the error inflation for "
f"'{phot_key}' but the argument of "
"'model' is set to 'None'. Inflation "
"of the errors is therefore not possible."
)
else:
readmodel = ReadModel(model, filter_name=phot_key)
model_flux = readmodel.get_flux(model_param)[0]
if f"error_{phot_key}" in model_param:
infl_factor = model_param[f"error_{phot_key}"]
else:
infl_factor = 10.0 ** model_param[f"log_error_{phot_key}"]
var_add = infl_factor**2 * model_flux**2
elif (
f"error_{instr_name}" in model_param
or f"log_error_{instr_name}" in model_param
):
# Inflate photometric uncertainty of an instrument
if model is None:
warnings.warn(
"The dictionary with model parameters "
"contains the error inflation for "
f"'{instr_name}' but the argument of "
"'model' is set to 'None'. Inflation "
"of the errors is therefore not possible."
)
var_add = None
else:
readmodel = ReadModel(model, filter_name=phot_key)
model_flux = readmodel.get_flux(model_param)[0]
if f"error_{instr_name}" in model_param:
infl_factor = model_param[f"error_{instr_name}"]
else:
infl_factor = 10.0 ** model_param[f"log_error_{instr_name}"]
var_add = infl_factor**2 * model_flux**2
else:
# No inflation required
var_add = None
if var_add is not None:
if infl_factor < 0.01:
message = (
f"Inflating the uncertainty of {phot_key} by a "
+ f"factor {infl_factor:.2e}..."
)
else:
message = (
f"Inflating the uncertainty of {phot_key} by a "
+ f"factor {infl_factor:.2f}..."
)
print(message, end="", flush=True)
phot_value[1] = np.sqrt(phot_value[1] ** 2 + var_add)
print(" [DONE]")
objectbox.flux[phot_key] = phot_value
if objectbox.spectrum is not None:
# Check if there are any spectra
for spec_key, spec_value in objectbox.spectrum.items():
# Get the spectrum (3 columns)
spec_tmp = spec_value[0]
if f"scaling_{spec_key}" in model_param:
# Scale the fluxes and uncertainties of the spectrum
scaling = model_param[f"scaling_{spec_key}"]
if scaling < 0.01:
print(
f"Scaling the flux of {spec_key} by: {scaling:.2e}...",
end="",
flush=True,
)
else:
print(
f"Scaling the flux of {spec_key} by: {scaling:.2f}...",
end="",
flush=True,
)
spec_tmp[:, 1] *= model_param[f"scaling_{spec_key}"]
spec_tmp[:, 2] *= model_param[f"scaling_{spec_key}"]
print(" [DONE]")
if (
f"error_{spec_key}" in model_param
or f"log_error_{spec_key}" in model_param
):
if model is None:
warnings.warn(
"The dictionary with model parameters "
"contains the error inflation for "
f"'{spec_key}' but the argument of "
"'model' is set to 'None'. Inflation "
"of the errors is therefore not possible."
)
elif model == "petitradtrans":
# Increase the errors by a constant value
add_error = 10.0 ** model_param[f"error_{spec_key}"]
log_msg = (
f"Inflating the uncertainties of {spec_key} "
+ "by a constant value of "
+ f"{add_error:.2e} (W m-2 um-1)..."
)
print(log_msg, end="", flush=True)
spec_tmp[:, 2] += add_error
print(" [DONE]")
else:
# Calculate the model spectrum
wavel_range = (0.9 * spec_tmp[0, 0], 1.1 * spec_tmp[-1, 0])
readmodel = ReadModel(model, wavel_range=wavel_range)
model_box = readmodel.get_model(
model_param,
spec_res=spec_value[3],
wavel_resample=spec_tmp[:, 0],
)
# Inflate the uncertainties relative to
# the fluxes of the model spectrum
if f"error_{spec_key}" in model_param:
infl_factor = model_param[f"error_{spec_key}"]
else:
infl_factor = 10.0 ** model_param[f"log_error_{spec_key}"]
log_msg = (
f"Inflating the uncertainties of {spec_key} "
+ f"by a factor {infl_factor:.2f}..."
)
print(log_msg, end="", flush=True)
spec_tmp[:, 2] = np.sqrt(
spec_tmp[:, 2] ** 2 + (infl_factor * model_box.flux) ** 2
)
print(" [DONE]")
# Store the spectra with the scaled fluxes and/or errors
# The other three elements (i.e. the covariance matrix,
# the inverted covariance matrix, and the spectral
# resolution) remain unaffected
objectbox.spectrum[spec_key] = (
spec_tmp,
spec_value[1],
spec_value[2],
spec_value[3],
)
return objectbox