Source code for species.util.box_util
"""
Utility functions for boxes.
"""
import warnings
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, float], 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