Ejemplo n.º 1
0
    def _load_table_(self, source):
        t = Table(self.source)
        data = {}
        for k in list(t.keys()):
            data[k] = t[k]
        # Alias columns
        data["logM"] = log10(np.asarray(data["M_ini"]))
        data["logg"] = np.asarray(data["logG"])
        data["logT"] = np.asarray(data["logTe"])
        data["logL"] = np.asarray(data["logL/Lo"])
        data["logA"] = np.asarray(data["log(age/yr)"])
        # clean columns
        data.pop("log(age/yr)")
        data.pop("M_ini")
        data.pop("logG")
        data.pop("logTe")
        data.pop("logL/Lo")

        self.data = Table(data, name="Isochrone from %s" % self.name)
Ejemplo n.º 2
0
class ezIsoch(Isochrone):
    """ Trying to make something that is easy to manipulate
    This class is basically a proxy to a table (whatever format works best)
    and tries to keep things coherent.
    """
    def __init__(self, source, interp=False):
        super().__init__()
        self.name = "<auto>"
        self.source = source
        self._load_table_(self.source)
        # round because of precision noise
        self.logages = np.unique(np.round(self.data["logA"], 6))
        self.ages = np.round(10**self.logages)
        self.Z = np.unique(np.round(self.data["Z"], 6))
        self.interpolation(interp)

    def selectWhere(self, *args, **kwargs):
        return self.data.selectWhere(*args, **kwargs)

    def interpolation(self, b=None):
        if b is not None:
            if hasattr(self, "interp"):
                print("Do not use interpolation yet, at your own risks!!")
            self.interp = bool(b)
        else:
            return self.interp

    def _load_table_(self, source):
        self.data = Table(self.source).selectWhere("*", "isfinite(logA)")

    def __getitem__(self, key):
        return self.data[key]

    def _get_t_isochrone(self,
                         age,
                         metal=None,
                         FeH=None,
                         masses=None,
                         *args,
                         **kwargs):
        """ Retrieve isochrone from the original source
            internal use to adapt any library
        """
        # make sure unit is in years and then only give the value (no units)
        _age = int(units.Quantity(age, units.year).value)

        #        if hasUnit(age):
        #            _age = int(age.to('yr').magnitude)
        #        else:
        #            _age = int(age * inputUnit.to('yr').magnitude)

        _logA = np.log10(_age)

        assert (metal
                is not None) | (FeH is not None), "Need a chemical par. value."

        if (metal is not None) & (FeH is not None):
            print("Warning: both Z & [Fe/H] provided, ignoring [Fe/H].")

        if metal is None:
            metal = self.FeHtometal(FeH)

        if self.interpolation():
            # Do the actual nd interpolation

            # Maybe already exists?
            if (metal in self.Z) & (_age in self.ages):
                t = self.selectWhere(
                    "*",
                    "(round(Z, 6) == {0}) & (round(logA, 6) == {1})".format(
                        metal, _logA),
                )
                if t.nrows > 0:
                    return t
            # apparently not
            # find 2 closest metal values
            ca1 = self.ages <= _age
            ca2 = self.ages > _age
            cz1 = self.Z <= metal
            cz2 = self.Z > metal
            if metal in self.Z:
                # perfect match in metal, need to find ages
                if _age in self.ages:
                    return self.selectWhere(
                        "*",
                        "(round(Z, 6) == {0}) & (round(logA, 6) == {1})".
                        format(metal, _logA),
                    )
                elif (True in ca1) & (True in ca2):
                    # bracket on _age: closest values
                    a1, a2 = (
                        np.log10(max(self.ages[ca1])),
                        np.log10(min(self.ages[ca2])),
                    )
                    iso = self.selectWhere(
                        "*",
                        "(Z == 0.02) & ( (abs(logA - {0}) < 1e-4) | (abs(logA - {1}) < 1e-4 )  )"
                        .format(a1, a2),
                    )
                    if masses is None:
                        _logM = np.unique(iso["logM"])
                    else:
                        _logM = masses

                    # define interpolator
                    points = np.array([self[k]
                                       for k in "logA logM Z".split()]).T
                    values = np.array(
                        [self[k] for k in list(self.data.keys())]).T
                    _ifunc = interpolate.LinearNDInterpolator(points, values)

                    pts = np.array([(_logA, logMk, metal) for logMk in _logM])
                    r = _ifunc(pts)
                    return Table(r)
                else:
                    raise Exception("Age not covered by the isochrones")
            elif (True in cz1) & (True in cz2):
                # need to find closest Z
                pass
            return
        else:
            # find the closest match
            _Z = self.Z[((metal - self.Z)**2).argmin()]
            # _logA = np.log10(self.ages[((_age - self.ages) ** 2).argmin()])
            _logA = self.logages[((np.log10(_age) - self.logages)**2).argmin()]
            tab = self.data.selectWhere(
                "*", "(round(Z, 6) == {0}) & (round(logA,6) == {1})".format(
                    _Z, _logA))
            # mass selection
            if masses is not None:
                # masses are expected in logM for interpolation
                # if masses.max() > 2.3:
                #    _m = np.log10(masses)
                # else:
                _m = masses
                data_logM = tab["logM"][:]
                # refuse extrapolation!
                # ind = np.where(_m <= max(data_logM))
                data = {}
                for kn in list(tab.keys()):
                    data[kn] = interp(_m,
                                      data_logM,
                                      tab[kn],
                                      left=np.nan,
                                      right=np.nan)
                return Table(data)
Ejemplo n.º 3
0
class CacheBackend(GridBackend):
    """CacheBackend -- Load content from a file only when needed

    The key idea is to be able to load the content only at the first query

    Currently the grid attribute is an eztable.Table object as it was before.
    """
    def __init__(self, fname, *args, **kwargs):
        """__init__

        Parameters
        ----------

        fname: str
            FITS or HD5 file containing the grid
        """
        super(CacheBackend, self).__init__()

        self.fname = fname
        self._type = self._get_type(fname)
        self.clear()

    def clear(self, attrname=None):
        """clear current cache

        Parameters
        ----------

        attrname: str in [lamb, filters, grid, header, lamb, seds]
            if provided clear only one attribute
            else all cache will be erased
        """
        if attrname is None:
            self._seds = None
            self._lamb = None
            self._filters = None
            self._grid = None
            self._header = None
        else:
            setattr(self, "_{0}".format(attrname), None)

    def _load_seds(self, fname):
        """load_seds - load seds"""
        if self._seds is None:
            if self._get_type(fname) == "fits":
                with pyfits.open(self.fname) as f:
                    self._seds = f[0].data[:-1]

            elif self._get_type(fname) == "hdf":
                with HDFStore(self.fname, mode="r") as s:
                    self._seds = s["/seds"].read()

    def _load_lamb(self, fname):
        """load_seds - load wavelength"""
        if self._lamb is None:
            if self._get_type(fname) == "fits":
                with pyfits.open(self.fname) as f:
                    self._lamb = f[0].data[-1]

            elif self._get_type(fname) == "hdf":
                with HDFStore(self.fname, mode="r") as s:
                    self._lamb = s["/lamb"].read()

    def _load_grid(self, fname):
        """load_grid - load grid table"""
        # load_seds - load wavelength and seds
        if self._grid is None:
            if self._get_type(fname) == "fits":
                self._grid = Table(self.fname)

            elif self._get_type(fname) == "hdf":
                self._grid = Table(self.fname, tablename="/grid")

    def _load_filters(self, fname):
        """load_filters -- load only filters"""
        if self._filters is None:
            if self._type == "fits":
                with pyfits.open(self.fname) as f:
                    self._filters = f[1].header.get(
                        "FILTERS", None) or f[1].header.get("filters", None)
                    if self._filters is not None:
                        self._filters = self._filters.split()
            elif self._type == "hdf":
                self._filters = self.header.get(
                    "FILTERS", None) or self.header.get("filters", None)
                if self._filters is not None:
                    self._filters = self._filters.split()

    @property
    def seds(self):
        """seds - load in cache if needed """
        self._load_seds(self.fname)
        return self._seds

    @seds.setter
    def seds(self, value):
        """ replace seds value """
        self._seds = value

    @property
    def lamb(self):
        """lamb - load in cache if needed """
        self._load_lamb(self.fname)
        return self._lamb

    @lamb.setter
    def lamb(self, value):
        """ replace seds value """
        self._lamb = value

    @property
    def grid(self):
        """grid - load in cache if needed """
        self._load_grid(self.fname)
        return self._grid

    @grid.setter
    def grid(self, value):
        """ replace seds value """
        self._grid = value

    @property
    def header(self):
        """header - load in cache if needed """
        self._load_grid(self.fname)
        return self._grid.header

    @header.setter
    def header(self, value):
        """ replace seds value """
        self._header = value

    @property
    def filters(self):
        """filters - load in cache if needed """
        self._load_filters(self.fname)
        return self._filters

    @filters.setter
    def filters(self, value):
        """ replace seds value """
        self._filters = value

    def keys(self):
        """ return column names when possible, avoid loading when possible """
        if hasattr(self._grid, "coldescrs"):
            return list(self._grid.coldescrs.keys())
        elif hasattr(self._grid, "keys"):
            return list(self._grid.keys())
        elif hasattr(self.grid, "keys"):
            return list(self.grid.keys())
        else:
            return []

    def writeFITS(self, fname, *args, **kwargs):
        """write -- export to fits file

        Parameters
        ----------

        fname: str
            filename (incl. path) to export to
        """
        if (self.lamb is not None) & (self.seds is not None) & (self.grid
                                                                is not None):
            if not isinstance(self.grid, Table):
                raise TypeError("Only eztables.Table are supported so far")
            r = numpy.vstack([self.seds, self.lamb])
            pyfits.writeto(fname, r, **kwargs)
            del r
            if getattr(self, "filters", None) is not None:
                if "FILTERS" not in list(self.grid.header.keys()):
                    self.grid.header["FILTERS"] = " ".join(self.filters)
            self.grid.write(fname, append=True)

    def writeHDF(self, fname, append=False, *args, **kwargs):
        """write -- export to HDF file

        Parameters
        ----------

        fname: str
            filename (incl. path) to export to

        append: bool, optional (default False)
            if set, it will append data to each Array or Table
        """
        if (self.lamb is not None) & (self.seds is not None) & (self.grid
                                                                is not None):
            if not isinstance(self.grid, Table):
                raise TypeError("Only eztables.Table are supported so far")
            with HDFStore(fname, mode="a") as hd:
                if not append:
                    hd["/seds"] = self.seds[:]
                    hd["/lamb"] = self.lamb[:]
                else:
                    try:
                        node = hd.get_node("/seds")
                        node.append(self.seds[:])
                    except Exception:
                        hd["/seds"] = self.seds[:]
                        hd["/lamb"] = self.lamb[:]
            if getattr(self, "filters", None) is not None:
                if "FILTERS" not in list(self.grid.header.keys()):
                    self.grid.header["FILTERS"] = " ".join(self.filters)
            self.grid.write(fname, tablename="grid", append=True)

    def copy(self):
        """ implement a copy method """
        g = CacheBackend(self.fname)
        g._aliases = copy.deepcopy(self._aliases)
        if self._grid is not None:
            g._grid = copy.deepcopy(self._grid)
        if self._seds is not None:
            g._seds = copy.deepcopy(self._seds)
        if self._lamb is not None:
            g._lamb = copy.deepcopy(self._lamb)
        if self._header is not None:
            g._header = copy.deepcopy(self._header)
        if self._filters is not None:
            g._filters = copy.deepcopy(self._filters)

        return g