"""We provide various modeling schemes for extinction in a given photometric band.
.. todo::
* add script to generate grid of models
* add polynomial training.
* compare literature values to ours.
"""
from pkg_resources import resource_filename
from glob import glob
from typing import Union, Sequence
from ..io import ecsv
from .polynomial import PolynomialModel
from .basemodel import _BaseModel
_DATA_PATH_ = resource_filename('dustapprox', 'data/precomputed')
kinds = {'polynomial': PolynomialModel,
}
[docs]class PrecomputedModel:
""" Access to precomputed models
.. code-block:: python
from dustapprox.models import PrecomputedModel
lib = PrecomputedModel()
# search for GALEX passbands if present
r = lib.find(passband='galex')
print(r)
# load both available models
models = []
for source in r.values():
models.extend([lib.load_model(r, passband=pbname) for pbname in source['passbands']])
.. code-block:: text
:caption: result from :func:`PrecomputedModel.find`
[{'atmosphere': {'source': 'Kurucz (ODFNEW/NOVER 2003)',
'teff': [3500.0, 50000.0],
'logg': [0.0, 5.0],
'feh': [-4, 0.5],
'alpha': [0, 0.4]},
'extinction': {'source': 'Fitzpatrick (1999)', 'R0': 3.1, 'A0': [0, 10]},
'comment': ['teffnorm = teff / 5040', 'predicts kx = Ax / A0'],
'model': {'kind': 'polynomial',
'degree': 3,
'interaction_only': False,
'include_bias': True,
'feature_names': ['A0', 'teffnorm']},
'passbands': ['GALEX_GALEX.FUV', 'GALEX_GALEX.NUV'],
'filename': 'dustapprox/data/precomputed/polynomial/f99/kurucz/kurucz_f99_a0_teff.ecsv'}]
.. code-block:: text
:caption: result when loading models with from :func:`PrecomputedModel.load_model`
[PolynomialModel: GALEX_GALEX.FUV
<dustapprox.models.polynomial.PolynomialModel object at 0x12917b6a0>
from: A0, teffnorm polynomial degree: 3,
PolynomialModel: GALEX_GALEX.NUV
<dustapprox.models.polynomial.PolynomialModel object at 0x129170820>
from: A0, teffnorm polynomial degree: 3]
"""
def __init__(self, location=None):
""" Constructor """
if location is None:
location = _DATA_PATH_
self._info = None
self.location = location
[docs] def get_models_info(self, glob_pattern='/**/*.ecsv') -> Sequence[dict]:
""" Retrieve the information for all models available and files """
if self._info is not None:
return self._info
location = self.location
lst = glob(f'{location:s}{glob_pattern:s}', recursive=True)
info = []
for fname in lst:
info.append(self._get_file_info(fname))
self._info = info
return info
def _get_file_info(self, fname:str) -> dict:
""" Extract information from a file """
info = {}
where = fname.replace(_DATA_PATH_, '')
df = ecsv.read(fname)
info = df.attrs.copy()
info['passbands'] = list(df['passband'].values)
info['filename'] = fname
return info
[docs] def find(self, passband=None, extinction=None, atmosphere=None, kind=None) -> Sequence[dict]:
""" Find all the computed models that match the given parameters.
The search is case insentive and returns all matches.
Parameters
----------
passband : str
The passband to be used.
extinction : str
The extinction model to be used. (e.g., 'Fitzpatrick')
atmosphere : str
The atmosphere model to be used. (e.g., 'kurucz')
kind : str
The kind of model to be used (e.g., polynomial).
Returns
-------
"""
info = self.get_models_info()
results = []
for value in info:
if passband is not None and passband.lower() not in ' '.join(value['passbands']).lower():
continue
if extinction is not None and extinction.lower() not in value['extinction']['source'].lower():
continue
if atmosphere is not None and atmosphere.lower() not in value['atmosphere']['source'].lower():
continue
if kind is not None and kind.lower() not in value['model']['kind'].lower():
continue
content = value.copy()
if passband is not None:
content['passbands'] = [pk for pk in content['passbands'] if passband.lower() in pk.lower()]
results.append(content)
return results
[docs] def load_model(self, fname: Union[str, dict],
passband: str = None):
""" Load a model from a file or description (:func:`PrecomputedModel.find`)
Parameters
----------
fname : str or dict
The filename of the model to be loaded or a description of the model
returned by :func:`PrecomputedModel.find`
passband : str
The passband to be loaded. If `None`, loads all available passband models.
Returns
-------
model : :class:`dustapprox.models.polynomial.PolynomialModel`
"""
if isinstance(fname, dict):
fname_ = fname['filename']
info = fname
else:
fname_ = fname
info = self._get_file_info(fname_)
model_kind = info['model']['kind']
if passband is None:
return [self.load_model(fname, pbname) for pbname in info['passbands']]
try:
return kinds[model_kind].from_file(fname_, passband=passband)
except KeyError:
raise NotImplementedError(f'Model kind {model_kind} not implemented.')