__init__.py 15.8 KB
Newer Older
Yannick Roehlly's avatar
Yannick Roehlly committed
1 2
# -*- coding: utf-8 -*-
"""
3
Copyright (C) 2012, 2013 Centre de données Astrophysiques de Marseille
Yannick Roehlly's avatar
Yannick Roehlly committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Licensed under the CeCILL-v2 licence - see Licence_CeCILL_V2-en.txt

@author: Yannick Roehlly <yannick.roehlly@oamp.fr>

This is the database where we store some data used by pcigale:
 - the information relative to the filters
 - the single stellar populations as defined in Marason (2005)
 - the infra-red templates from Dale and Helou (2002)

The classes for these various objects are described in pcigale.data
sub-packages. The corresponding underscored classes here are used by the
SqlAlchemy ORM to store the data in a unique SQLite3 database.

"""

import pkg_resources
from sqlalchemy import (create_engine, exc, Column, String, Text,
21
                        Float, Integer, PickleType)
Yannick Roehlly's avatar
Yannick Roehlly committed
22 23 24 25
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from .filters import Filter
from .ssp_m2005 import SspM2005
26
from .ssp_bc03 import SspBC03
Yannick Roehlly's avatar
Yannick Roehlly committed
27
from .ir_templates_dh2002 import IrTemplatesDH2002
28
from .ir_models_dl2007 import DL2007
29
from .agn_fritz2006 import AgnFritz2006
Yannick Roehlly's avatar
Yannick Roehlly committed
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66


DATABASE_FILE = pkg_resources.resource_filename(__name__, 'data.db')

ENGINE = create_engine('sqlite:///' + DATABASE_FILE, echo=False)
BASE = declarative_base()
SESSION = sessionmaker(bind=ENGINE)


class _Filter(BASE):
    """ Storage for filters
    """

    __tablename__ = 'filters'

    name = Column(String, primary_key=True)
    description = Column(String)
    trans_type = Column(String)
    trans_table = Column(PickleType)
    effective_wavelength = Column(Float)

    def __init__(self, f):
        self.name = f.name
        self.description = f.description
        self.trans_type = f.trans_type
        self.trans_table = f.trans_table
        self.effective_wavelength = f.effective_wavelength


class _SspM2005(BASE):
    """Storage for Maraston 2005 SSP
    """

    __tablename__ = 'maraston2005'

    imf = Column(String, primary_key=True)
    metallicity = Column(Float, primary_key=True)
Yannick Roehlly's avatar
Yannick Roehlly committed
67
    time_grid = Column(PickleType)
Yannick Roehlly's avatar
Yannick Roehlly committed
68 69 70 71 72 73 74
    wavelength_grid = Column(PickleType)
    mass_table = Column(PickleType)
    spec_table = Column(PickleType)

    def __init__(self, ssp):
        self.imf = ssp.imf
        self.metallicity = ssp.metallicity
Yannick Roehlly's avatar
Yannick Roehlly committed
75
        self.time_grid = ssp.time_grid
Yannick Roehlly's avatar
Yannick Roehlly committed
76 77 78 79 80
        self.wavelength_grid = ssp.wavelength_grid
        self.mass_table = ssp.mass_table
        self.spec_table = ssp.spec_table


81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
class _SspBC03(BASE):
    """Storage for Bruzual and Charlot 2003 SSP
    """

    __tablename__ = "bc03"

    imf = Column(String, primary_key=True)
    metallicity = Column(Float, primary_key=True)
    time_grid = Column(PickleType)
    wavelength_grid = Column(PickleType)
    color_table = Column(PickleType)
    lumin_table = Column(PickleType)

    def __init__(self, ssp):
        self.imf = ssp.imf
        self.metallicity = ssp.metallicity
        self.time_grid = ssp.time_grid
        self.wavelength_grid = ssp.wavelength_grid
        self.color_table = ssp.color_table
        self.lumin_table = ssp.lumin_table


Yannick Roehlly's avatar
Yannick Roehlly committed
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
class _DH2002InfraredTemplates(BASE):
    """Storage for Dale and Helou (2002) infra-red templates

    The Dale and Helou (2002) template are gathered in a unique numpy array,
    nevertheless, they are stored in their own table with a unique row.

    """

    __tablename__ = 'dh2002_templates'

    name = Column(String, primary_key=True)
    description = Column(Text)
    data = Column(PickleType)

    def __init__(self, name, description, data):
        self.name = name
        self.description = description
        self.data = data


123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
class _DL2007(BASE):
    """Storage for Draine and Li (2007) IR models
    """

    __tablename__ = 'DL2007_models'
    qpah = Column(Float, primary_key=True)
    umin = Column(Float, primary_key=True)
    umax = Column(Float, primary_key=True)
    wave = Column(PickleType)
    lumin = Column(PickleType)

    def __init__(self, model):
        self.qpah = model.qpah
        self.umin = model.umin
        self.umax = model.umax
        self.wave = model.wave
        self.lumin = model.lumin


142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
class _Fritz2006AGN(BASE):
    """Storage for Fritz et al. (2006) models
    """

    __tablename__ = 'fritz2006_agn'
    model_nb = Column(Integer, primary_key=True)
    agn_type = Column(Integer)
    r_ratio = Column(Float)
    tau = Column(Float)
    beta = Column(Float)
    gamma = Column(Float)
    theta = Column(Float)
    psy = Column(Float)
    wave = Column(PickleType)
    luminosity = Column(PickleType)

    def __init__(self, agn):
        self.model_nb = agn.model_nb
        self.agn_type = agn.agn_type
        self.r_ratio = agn.r_ratio
        self.tau = agn.tau
        self.beta = agn.beta
        self.gamma = agn.gamma
        self.theta = agn.theta
        self.psy = agn.psy
        self.wave = agn.wave
        self.luminosity = agn.luminosity


Yannick Roehlly's avatar
Yannick Roehlly committed
171 172 173 174 175 176 177
class Database(object):
    """Object giving access to pcigale database."""

    def __init__(self, writable=False):
        """
        Create a collection giving access to access the pcigale database.

Yannick Roehlly's avatar
Yannick Roehlly committed
178
        Parameters
Yannick Roehlly's avatar
Yannick Roehlly committed
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
        ----------
        writable : boolean
            If True the user will be able to write new data in the database
            (but he/she must have a writable access to the sqlite file). By
            default, False.
        """
        self.session = SESSION()
        self.is_writable = writable

    def upgrade_base(self):
        """ Upgrade the table schemas in the database
        """
        if self.is_writable:
            BASE.metadata.create_all(ENGINE)
        else:
            raise StandardError('The database is not writable.')

    def close(self):
        """ Close the connection to the database

        TODO: It would be better to wrap the database use inside a context
        manager.
        """
        self.session.close_all()

    def add_filter(self, pcigale_filter):
        """
        Add a filter to pcigale database.

Yannick Roehlly's avatar
Yannick Roehlly committed
208
        Parameters
Yannick Roehlly's avatar
Yannick Roehlly committed
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
        ----------
        pcigale_filter : pcigale.data.Filter
        """
        if self.is_writable:
            self.session.add(_Filter(pcigale_filter))
            try:
                self.session.commit()
            except exc.IntegrityError:
                self.session.rollback()
                raise StandardError('The filter is yet in the base.')
        else:
            raise StandardError('The database is not writable.')

    def add_ssp_m2005(self, ssp_m2005):
        """
        Add a Maraston 2005 SSP to pcigale database

Yannick Roehlly's avatar
Yannick Roehlly committed
226
        Parameters
Yannick Roehlly's avatar
Yannick Roehlly committed
227 228 229 230 231 232 233 234 235 236 237
        ----------
        ssp : pcigale.base.SspM2005

        """
        if self.is_writable:
            ssp = _SspM2005(ssp_m2005)
            self.session.add(ssp)
            try:
                self.session.commit()
            except exc.IntegrityError:
                self.session.rollback()
Yannick Roehlly's avatar
Yannick Roehlly committed
238
                raise StandardError('The SSP is already in the base.')
Yannick Roehlly's avatar
Yannick Roehlly committed
239 240 241
        else:
            raise StandardError('The database is not writable.')

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
    def add_ssp_bc03(self, ssp_bc03):
        """
        Add a Bruzual and Charlot 2003 SSP to pcigale database

        Parameters
        ----------
        ssp : pcigale.data.SspBC03

        """
        if self.is_writable:
            ssp = _SspBC03(ssp_bc03)
            self.session.add(ssp)
            try:
                self.session.commit()
            except exc.IntegrityError:
                self.session.rollback()
Yannick Roehlly's avatar
Yannick Roehlly committed
258
                raise StandardError('The SSP is already in the base.')
259 260 261
        else:
            raise StandardError('The database is not writable.')

Yannick Roehlly's avatar
Yannick Roehlly committed
262 263 264 265
    def add_dh2002_infrared_templates(self, data):
        """
        Add Dale and Helou (2002) templates the collection.

Yannick Roehlly's avatar
Yannick Roehlly committed
266
        Parameters
Yannick Roehlly's avatar
Yannick Roehlly committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
        ----------
        data : array
            Array containing the templates data.

        """
        name = 'dh2002'

        description = ("These are the Dale & Helou (2002) infra-red "
                       "templates to which the stellar emission was "
                       "subtracted (Nohl et al., 2009). The data is a "
                       "tuple composed of the alpha grid, the lambda grid "
                       "and a 2D array of luminosity density (normalised "
                       "over the full spectrum) with the alpha in the first "
                       "axis and the lambda in the second.")

        if self.is_writable:
            template = _DH2002InfraredTemplates(name, description, data)
            self.session.add(template)
            try:
                self.session.commit()
            except exc.IntegrityError:
                self.session.rollback()
Yannick Roehlly's avatar
Yannick Roehlly committed
289
                raise StandardError('The template is already in the base.')
Yannick Roehlly's avatar
Yannick Roehlly committed
290 291 292
        else:
            raise StandardError('The database is not writable.')

293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
    def add_dl2007(self, model):
        """
        Add a Draine and Li (2007) model to the databse.

        Parameters
        ----------
        model: pcigale.data.DL2007

        """
        if self.is_writable:
            self.session.add(_DL2007(model))
            try:
                self.session.commit()
            except exc.IntegrityError:
                self.session.rollback()
                raise StandardError('The DL07 model is already in the base.')
        else:
            raise StandardError('The database is not writable.')

312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
    def add_fritz2006_agn(self, agn):
        """
        Add a Fritz et al. (2006) AGN model to the database.

        Parameters
        ----------
        agn : pcigale.data.AgnFritz2006

        """
        if self.is_writable:
            self.session.add(_Fritz2006AGN(agn))
            try:
                self.session.commit()
            except exc.IntegrityError:
                self.session.rollback()
Yannick Roehlly's avatar
Yannick Roehlly committed
327
                raise StandardError('The agn model is already in the base.')
328 329 330
        else:
            raise StandardError('The database is not writable.')

Yannick Roehlly's avatar
Yannick Roehlly committed
331 332 333 334
    def get_filter(self, name):
        """
        Get a specific filter from the collection

Yannick Roehlly's avatar
Yannick Roehlly committed
335
        Parameters
Yannick Roehlly's avatar
Yannick Roehlly committed
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
        ----------
        name : string
            Name of the filter

        Returns
        -------
        filter : pcigale.base.Filter
            The Filter object. If the filter is not in the database,
            returns None.
        """
        result = (self.session.query(_Filter).
                  filter(_Filter.name == name).
                  first())
        if result:
            return Filter(result.name, result.description,
                          result.trans_type, result.trans_table,
                          result.effective_wavelength)
        else:
            return None

356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
    def get_ssp_bc03(self, imf, metallicity):
        """
        Query the database for the Bruzual and Charlot 2003 SSP corresponding
        to the given initial mass function and metallicity.

        Parameters
        ----------
        imf : string
            Initial mass function (salp for Salpeter, chab for Chabrier)
        metallicity : float
            0.02 for Solar metallicity
        Returns
        -------
        ssp : pcigale.data.SspBC03
            The SspBC03 object. If no SSP corresponds to the given imf and
            metallicity, returns None.

        """
        result = self.session.query(_SspBC03)\
            .filter(_SspBC03.imf == imf)\
            .filter(_SspBC03.metallicity == metallicity)\
            .first()
        if result:
            return SspBC03(result.imf, result.metallicity, result.time_grid,
                           result.wavelength_grid, result.color_table,
                           result.lumin_table)
        else:
            return None

Yannick Roehlly's avatar
Yannick Roehlly committed
385 386 387 388 389
    def get_ssp_m2005(self, imf, metallicity):
        """
        Query the database for a Maraston 2005 SSP corresponding to the given
        initial mass function and metallicity.

Yannick Roehlly's avatar
Yannick Roehlly committed
390
        Parameters
Yannick Roehlly's avatar
Yannick Roehlly committed
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
        ----------
        imf : string
            Initial mass function (ss for Salpeter, kr for Kroupa)
        metallicity : float
            [Z/H] = Log10(Z/Zsun) - Log10(H/Hsun)

        Returns
        -------
        ssp : pcigale.base.SspM2005
            The SspM2005 object. If no SSP corresponds to the given imf and
            metallicity, returns None.

        """
        result = self.session.query(_SspM2005)\
            .filter(_SspM2005.imf == imf)\
            .filter(_SspM2005.metallicity == metallicity)\
            .first()
        if result:
Yannick Roehlly's avatar
Yannick Roehlly committed
409
            return SspM2005(result.imf, result.metallicity, result.time_grid,
Yannick Roehlly's avatar
Yannick Roehlly committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
                            result.wavelength_grid, result.mass_table,
                            result.spec_table)
        else:
            return None

    def get_dh2002_infrared_templates(self):
        """
        Get the Dale and Helou infrared templates from the database

        Returns
        -------
        template : pcigale.base.IrTemplatesDH2002
            The Dale and Helou (2002) infrared templates. If they are not in
            the database, return None.

        """
        result = (self.session.query(_DH2002InfraredTemplates).
                  filter(_DH2002InfraredTemplates.name == 'dh2002').
                  first())
        if result:
            return IrTemplatesDH2002(result.data[0], result.data[1],
                                     result.data[2])
        else:
            return None

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
    def get_agn_fritz2006(self, model_nb):
        """
        Get the Fritz et al. (2006) AGN model corresponding to the number.

        Parameters
        ----------
        model_nb : integer
            Model number.

        Returns
        -------
        agn : pcigale.data.AgnFritz2006
            The AGN model or not if no model exists for the given number.

        """
        result = (self.session.query(_Fritz2006AGN).
                  filter(_Fritz2006AGN.model_nb == model_nb).
                  first())
        if result:
            return AgnFritz2006(result.model_nb, result.agn_type,
                                result.r_ratio, result.tau, result.beta,
                                result.gamma, result.theta, result.psy,
                                result.wave, result.luminosity)
        else:
            return None

461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
    def get_dl2007(self, qpah, umin, umax):
        """
        Get the Draine and Li (2007) model corresponding to the given set of
        paramters.

        Parameters
        ----------
        qpah: float
            Mass fraction of PAH
        umin: float
            Minimum radiation field
        umax: float
            Maximum radiation field
        gamma: float
            Fraction of the dust exposed from Umin to Umax

        """
        result = (self.session.query(_DL2007).
                  filter(_DL2007.qpah == qpah).
                  filter(_DL2007.umin == umin).
                  filter(_DL2007.umax == umax).
                  first())
        if result:
            return DL2007(result.qpah, result.umin, result.umax, result.wave,
                          result.lumin)
        else:
            return None

Yannick Roehlly's avatar
Yannick Roehlly committed
489 490 491 492 493
    def get_filter_list(self):
        """Get the list of the filters in the database.

        Returns
        -------
Yannick Roehlly's avatar
Yannick Roehlly committed
494
        names, lamdba_eff : array, dictionary
Yannick Roehlly's avatar
Yannick Roehlly committed
495
            names is the list of the filter names and lambda_eff is a
Yannick Roehlly's avatar
Yannick Roehlly committed
496
            dictionary associating the effective wavelength (in nm) to the
Yannick Roehlly's avatar
Yannick Roehlly committed
497 498 499 500 501 502 503 504 505
            filter name
        """
        result = self.session.query(_Filter.name,
                                    _Filter.effective_wavelength).all()
        result = dict(result)
        return result.keys(), result

    def parse_filters(self):
        """Generator to parse the filter database."""
506
        for filt in self.session.query(_Filter):
Yannick Roehlly's avatar
Yannick Roehlly committed
507 508 509 510 511
            yield Filter(filt.name, filt.description, filt.trans_type,
                         filt.trans_table, filt.effective_wavelength)

    def parse_ssp_m2005(self):
        """Generator to parse the Maraston 2005 SSP database."""
512
        for ssp in self.session.query(_SspM2005):
Yannick Roehlly's avatar
Yannick Roehlly committed
513
            yield SspM2005(ssp.imf, ssp.metallicity, ssp.time_grid,
Yannick Roehlly's avatar
Yannick Roehlly committed
514 515
                           ssp.wavelength_grid, ssp.mass_table,
                           ssp.spec_table)