def evaluate(self, co):
        """
      Evaluates the model for current parameter values.
      
      Parameters
      ----------
      co : array
           Specifies the points at which to evaluate the model.
    """

        if (self["sig_core"] <= 0.0) or (self["sig_wing"] <= 0.0):
            raise(PE.PyAValError("Width(s) of Gaussian must be larger than zero.", \
                                 solution="Change width ('sig_core/y')."))
        if self["f_core"] > 1.0:
            raise(PE.PyAValError("The relative normalization of the core and wing components shuold be between 0 and 1.", \
                                 solution="Set limits of f_core."))
        f_wing = 1 - self["f_core"]
        result = self["A"] * ( \
          self["f_core"] / \
        ((1 + ((co[::,::,0] - self["x0"])**2 + (co[::,::,1] - self["y0"])**2)/(self["sig_core"]**2)\
          ) ** 2) + \
        f_wing / \
        ((1 + ((co[::,::,0] - self["x0"])**2 + (co[::,::,1] - self["y0"])**2)/(self["sig_wing"]**2)\
          ) ** 2)
        )
        return result
Exemple #2
0
def getKuruczModel(teff, logg, met, nameadd=""):
    """
    Obtain a Kurucz model
    
    Parameters
    ----------
    teff : float
        Effective temperature [K]
    logg : float
        Logarithmic surface gravity [cgs]
    met : float
        Logarithmic metallicity, e.g., +0.1.
    nameadd : string, optional
        Name extension of the model grid; for instance,
        "NOVER".
    
    Returns
    -------
    Model : list of strings
        The requested model.
  """
    km = KuruczModels()
    if not km.gridAvailable(met, nameadd):
        raise(PE.PyAValError("No model grid with logarithmic metallicity of " + str(met) + \
                             " and name addition " + str(nameadd) + " available."))
    mt = km.requestModelGrid(met, nameadd)
    if not mt.modelAvailable(teff, logg, met):
        raise(PE.PyAValError("No model available in grid for parameters: teff = %6.3e, logg = %6.3e, met = %6.3e " \
                             % (teff, logg, met), \
                             solution=["Available Teffs: " + str(list(mt.availableTeffs())), \
                                       "Available Loggs: " + str(list(mt.availableLoggs()))]))
    return mt.getModel(teff, logg, met)
    def __init__(self, los='-z'):

        if isinstance(los, six.string_types):
            # The input is a string. Check the content.
            r = re.match("^([-+])([xyz])$", los)
            if r is None:
                raise (PE.PyAValError("A line of sight encoded by " + los +
                                      " cannot be constructed."))
            if r.group(2) == "x":
                self.los = np.array([1.0, 0.0, 0.0])
            elif r.group(2) == "y":
                self.los = np.array([0.0, 1.0, 0.0])
            elif r.group(2) == "z":
                self.los = np.array([0.0, 0.0, 1.0])

            if r.group(1) == "-":
                self.los *= -1.0

            self.losName = los
            return

        # Try to convert input to numpy array
        convlos = np.array(los)
        # Check length
        if len(convlos) != 3:
            raise (PE.PyAValError(
                "A line of sight argument does not have the right length of 3."
            ))
        # Check that it is a unit vector
        if np.abs(np.sum(convlos**2) - 1.0) > 1e-8:
            raise (PE.PyAValError(
                "A line of sight argument is not a unit vector."))

        self.los = convlos
        self.losName = str(convlos)
def hmsToDeg(h, m, s):
    """
    Convert hour-minute-second specification into degrees.
    
    Parameters
    ----------
    h : float
        Hours (0-24)
    m : float
        Minutes (time, 0-60)
    s : float
        Seconds (time, 0-60)
    
    Returns
    -------
    Angle : float
        The corresponding angle in degrees.
  """
    if (h < 0.0) or (h >= 24.0):
        raise(PE.PyAValError("Hour (" + str(h) + ") out of range (0 <= h < 24).", \
                             solution="Specify a value between 0 and 24."))
    if (m < 0.0) or (m >= 60.0):
        raise(PE.PyAValError("Minute (" + str(m) + ") out of range (0 <= m < 60)", \
                             solution="Specify a value between 0 and 60."))
    if (s < 0.0) or (s >= 60.0):
        raise(PE.PyAValError("Second (" + str(s) + ") out of range (0 <= m < 60)", \
                             solution="Specify a value between 0 and 60."))
    result = h * 15.0 + m * 0.25 + s * (0.25 / 60.0)
    return result
def isInTransit(time, T0, period, halfDuration, boolOutput=False, secin=False):
    """
    Check whether time is inclosed by transit interval.

    This function uses the given ephemerides (T0, period, and
    halfDuration) to check whether the time point(s)
    specified by `time` are within a transit window or not.
    The edges of the window are counted as transit times.

    Parameters
    ----------
    time : float or array like
        The time(s) which are to be checked.
    T0 : float
        The time reference point (center of transit).
    period : float
        The orbital period.
    halfDuration : float
        The half-duration of the event.
        Must have same units as `time`.
    boolOutput : boolean, optional
        If set True and `time` is an array, the function will
        return a bool array holding True for time points in-
        and False for time points out-of-transit.
    secin : boolean, optional
        If True, also points associated with the secondary
        transit (around phase 0.5) will be counted as falling
        into the transit window. Default is False.

    Returns
    -------
    inTransit : boolean or array of int
        If `time` was a float, the return value is a boolean,
        which is True if the give time falls into a transit
        interval and False otherwise.
        If `time` was given as an array, the return value is
        an array holding the indices of those time points,
        which fall into a transit window. The `boolOutput`
        option may be used to obtain a boolean array holding
        True for in-transit points. 

    """
    if halfDuration > period / 2.:
        raise(PE.PyAValError("The half-duration is longer than half the period. This cannot be true.",
                             where="isInTransit"))
    if (period <= 0.0) or (halfDuration <= 0.0):
        raise(PE.PyAValError("Both period and half-duration must be larger 0.",
                             where="isInTransit"))
    absPhase = np.array(np.abs((np.array(time) - T0) / period), ndmin=1)
    absPhase -= np.floor(absPhase)
    dPhase = halfDuration / period
    isIn = np.logical_or(absPhase <= dPhase, absPhase >= (1. - dPhase))
    if secin:
        isIn = isIn | (np.abs(absPhase - 0.5) < dPhase)
    indi = np.where(isIn)[0]
    if isinstance(time, float):
        return (len(indi) == 1)
    if boolOutput:
        return isIn
    return indi
    def getElSymbol(self, atn):
        """
      Convert atomic number into elemental symbol.
      
      Parameters
      ----------
      atn : int
          Atomic number
          
      Returns
      -------
      Symbol : string
          Elemental symbol
    """
        try:
            atn = int(atn)
        except ValueError:
            raise (PE.PyAValError("`atn` needs to be an integer. You gave: " +
                                  str(atn)))

        if not atn in self._data:
            raise (PE.PyAValError("No such atomic number in the data base: " +
                                  str(atn)))

        return self._data[atn]
def dmsToDeg(d, m, s):
    """
    Convert degree-arcminute-arcsecond specification into degrees.
    
    Parameters
    ----------
    d : float
        Degrees.
    m : float
        Arcminutes (0-60)
    s : float
        Arcseconds (0-60)
    
    Returns
    -------
    Angle : float
        The corresponding angle in degrees.
  """
    if (m < 0.0) or (m >= 60.0):
        raise(PE.PyAValError("Minute (" + str(m) + ") out of range (0 <= m < 60)", \
                             solution="Specify a value between 0 and 60."))
    if (s < 0.0) or (s >= 60.0):
        raise(PE.PyAValError("Second (" + str(s) + ") out of range (0 <= m < 60)", \
                             solution="Specify a value between 0 and 60."))
    sign = 1.0
    if d < 0.0:
        sign = -1.0

    result = d + sign * (m / 60.0) + sign * (s / 3600.0)
    return result
Exemple #8
0
 def logRphotNoyes(self, bv, lc="ms"):
     """
   Photospheric contribution to surface flux in the H and K pass-bands.
   
   Relation given by Noyes et al. 1984.
   
   Parameters
   ----------
   bv : float
       B-V color [mag]
   lc : string, {ms, g}, optional
       Luminosity class.
   
   Returns
   -------
   log10(Rphot) : float
       Logarithm of the photospheric contribution.
 """
     if (bv < 0.44) or (bv > 0.82):
         PE.warn(PE.PyAValError("Noyes et al. 1984 give a validity range of 0.44 < B-V < 0.82 for the " + \
                                "photospheric correction. However, the authors use it for B-V > 0.82, " + \
                                "where it quickly decreases."))
     if lc != "ms":
         PE.warn(
             PE.PyAValError(
                 "Noyes et al. 1984 specify the photospheric correction only for main-sequence stars."
             ))
     rp = -4.898 + 1.918 * bv**2 - 2.893 * bv**3
     return rp
    def getElementName(self, atn):
        """
      Convert atomic number into elemental name.
      
      Parameters
      ----------
      atn : int
          Atomic number
          
      Returns
      -------
      Name : string
          Name of element
    """
        try:
            atn = int(atn)
        except ValueError:
            raise (PE.PyAValError("`atn` needs to be an integer. You gave: " +
                                  str(atn)))

        if not atn in self._data:
            raise (PE.PyAValError("No such atomic number in the data base: " +
                                  str(atn)))

        return self._elNames[atn]
    def evaluate(self, co):
        """
      Evaluates the model for current parameter values.
      
      Parameters
      ----------
      co : array
           Specifies the points at which to evaluate the model.
    """
        if (self["sigx"] <= 0.0) or (self["sigy"] <= 0.0):
            raise(PE.PyAValError("Width(s) of Gaussian must be larger than zero.", \
                                 solution="Set limits."))
        if self["theta"] > 2. * np.pi:
            raise(PE.PyAValError("The angle parameter must be 0 <= theta <= 2pi.", \
                                 solution="Set limits."))
        a = np.cos(self['theta'])**2 / (2 * self['sigx']**2) + \
            np.sin(self['theta'])**2 / (2*self['sigy']**2)
        b = np.cos(2.*self['theta']) / (4 * self['sigx']**2) + \
            np.sin(2.*self['theta']) / (4 * self['sigy']**2)
        c = np.sin(self['theta'])**2 / (2 * self['sigx']**2) + \
            np.cos(self['theta'])**2 / (2 * self['sigy']**2)

        result = self["A"] * np.exp(\
          -1. * (a*(co[::,::,0]-self['x0'])**2 - 2*b*(co[::,::,0]-self['x0'])*(co[::,::,1]-self['y0']) + c*(co[::,::,1]-self['y0'])**2) \
          ) + self['const']
        return result
Exemple #11
0
    def evaluate(self, co):
        """
      Evaluates the model for current parameter values.
      
      Parameters
      ----------
      co : array
           Specifies the points at which to evaluate the model.
    """

        result = ones((shape(co)[0], shape(co)[1])) * self["off"]

        for i in range(self.n):
            p = str(i + 1)
            if (self["sigx" + p] <= 0.0) or (self["sigy" + p] <= 0.0):
                raise(PE.PyAValError("Width(s) of Gaussian must be larger than zero.", \
                                    solution="Change width ('sigx/y')."))
            if self["rho" + p] > 1.0:
                raise(PE.PyAValError("The correlation coefficient must be 0 <= rho <= 1.", \
                                    solution="Change width ('sigx/y')."))

            result += self["A"+p]/(2.*pi*self["sigx"+p]*self["sigy"+p]*sqrt(1.-self["rho"+p]**2)) * \
                exp( ((co[::,::,0]-self["mux"+p])**2/self["sigx"+p]**2 + (co[::,::,1]-self["muy"+p])**2/self["sigy"+p]**2 - \
                    2.*self["rho"+p]*(co[::,::,0]-self["mux"+p])*(co[::,::,1]-self["muy"+p])/(self["sigx"+p]*self["sigy"+p])) / \
                    (-2.*(1.-self["rho"+p]**2)) )
        return result
Exemple #12
0
def magToFluxDensity_bessel98(band, mag, mode="nu"):
    """
    Convert magnitude into flux density according to Bessel et al. 1998
    
    The conversion implemented here is based on the data given in Table A2 of
    Bessel et al. 1998, A&A 333, 231-250, which gives "Effective wavelengths (for an A0 star),
    absolute fluxes (corresponding to zero magnitude) and zeropoint magnitudes for the UBVRI-
    JHKL Cousins-Glass-Johnson system". Note that zp(f_nu) and zp(f_lam) are exchanged in
    the original table.

    Parameters
    ----------
    band : string
        Any of U, B, V, R, I, J, H, K, Kp, L, and L*
    mag : float, array
        The magnitude value to be converted
    mode : string, {nu, mod}
        Determines whether f_nu or f_lam will be calculated.

    Returns
    -------
    f_nu/lam : float
        The corresponding flux density in units if erg/cm**2/s/Hz in the
        case of mode 'nu' and erg/cm**2/s/A in the case of 'lam'. 
    lam_eff : float
        Effective filter wavelength in Angstrom
    """
    if not mode in ["nu", "lam"]:
        raise(PE.PyAValError("Unknown mode", \
                             where="magToFluxDensity_bessel98", \
                             solution="Use 'nu' or 'lam'"))

    # Data from Bessel 1998 Table A2
    d = """bands U B V R I J H K Kp L L*
    lam_eff 0.366 0.438 0.545 0.641 0.798 1.22 1.63 2.19 2.12 3.45 3.80
    f_nu 1.790 4.063 3.636 3.064 2.416 1.589 1.021 0.640 0.676 0.285 0.238
    f_lam 417.5 632 363.1 217.7 112.6 31.47 11.38 3.961 4.479 0.708 0.489
    zp(f_nu) 0.770 -0.120 0.000 0.186 0.444 0.899 1.379 1.886 1.826 2.765 2.961
    zp(f_lam) -0.152 -0.602 0.000 0.555 1.271 2.655 3.760 4.906 4.780 6.775 7.177"""
    # Digest table
    s = d.split()
    t = {}
    for i in range(6):
        t[s[i * 12]] = s[i * 12 + 1:(i + 1) * 12]
        if s[i * 12] != "bands":
            t[s[i * 12]] = np.array(t[s[i * 12]], dtype=np.float)

    if not band in t["bands"]:
        raise(PE.PyAValError("Unknown passband: " + str(band), \
                             where="magToFluxDensity_bessel98", \
                             solution="Use any of: " + ", ".join(t["bands"])))

    # Band index
    bi = t["bands"].index(band)

    c = {"nu": 48.598, "lam": 21.1}

    f = 10.0**((mag + c[mode] + t["zp(f_" + mode + ")"][bi]) / -2.5)
    return f, t["lam_eff"][bi] * 1e4
Exemple #13
0
 def getBroadeningFunction(self, flux, wlimit=0.0, asarray=False):
     """
   Calculate the broadening function.
   
   .. note:: The `decompose` method has to be called
             first. On this call, the template is
             specified.
   
   Parameters
   ----------
   flux : array or matrix
       The observed function (flux).
   wlimit : float, optional
       A limit to be applied to the singular
       values. Values smaller than the specified
       limit will be discarded in calculating
       the broadening function.
   asarray : bool, optional
       If True, the broadening function will be returned
       as an array instead of a matrix. The default is
       False.
   
   Returns
   -------
   Broadening function : matrix, array
       The broadening function. The return type can be set
       by the `asarray` flag.
 """
     if len(flux) == (len(self.template) - self.bn + 1):
         validIndi = np.arange(len(self.template) - self.bn + 1)
     elif len(flux) == len(self.template):
         validIndi = np.arange(self.bn // 2, len(flux) - self.bn // 2)
     else:
         raise(PE.PyAValError("Inappropriate length of flux array.\n" + \
                              "  Either len(template) or len(template)-bn+1 is accepted.", \
                              solution="Adjust the length of the input spectrum."))
     if self.w is None:
         raise(PE.PyAValError("There is no vector of singular values yet.", \
                              solution="Call `decompose` first."))
     w1 = 1.0 / self.w
     indi = np.where(self.w < wlimit)[0]
     w1[indi] = 0.0
     b = np.dot(
         self.v,
         np.dot(np.diag(w1), np.dot(self.u.T,
                                    np.matrix(flux[validIndi]).T)))
     if not asarray:
         return b
     else:
         return b.getA()[::, 0]
def coordsSexaToDeg(c, fullOut=False):
    """
    Convert sexagesimal coordinate string into degrees.
    
    Parameters
    ----------
    c : string
        The coordinate string. Valid formats are, e.g.,
        "00 05 08.83239 +67 50 24.0135" or "00:05:08.83239 -67:50:24.0135".
        Spaces or colons are allowed as separators for the individual
        components of the coordinates.
    fullOut : boolean, optional
        If True, two additional tuples holding the individual components of
        the right ascension and declination specification will be returned.
        The default is False.
    
    Returns
    -------
    ra, dec : float
        Right ascension and declination in degrees.
    hms, dms : tuples of three floats
        If `fullOut` is True, two tuples of three numbers each will be
        returned, which hold the individual constituents making up the
        right ascension (hms) and declination (dms) specifiers in sexagesimal
        representation.
    """
    r = re.match(
        "^\s*(\d+)([\s:])(\d+)([\s:])(\d+(\.\d*)?)\s+(([+-])(\d+))([\s:])(\d+)([\s:])(\d+(\.\d*)?)\s*$",
        c)
    if r is None:
        raise (PE.PyAValError(
            "Could not decompose coordinate string: \"" + str(c) + "\"",
            solution=
            "Use, e.g., 00 05 08.83239 +67 50 24.0135 or 00:05:08.83239 +67:50:24.0135"
        ))
    # Check separator consistency
    for n in [4, 10, 12]:
        if r.group(2) != r.group(n):
            raise (PE.PyAValError(
                "Separators (space and colon) mixed in coordinate definition.",
                solution="Use consistent separator."))

    ra = (float(r.group(1)), float(r.group(3)), float(r.group(5)))
    esign = {'+': +1, '-': -1}[r.group(8)]
    de = (float(r.group(7)), float(r.group(11)), float(r.group(13)), esign)
    result = (hmsToDeg(*ra), dmsToDeg(*de))
    if not fullOut:
        return result
    return result[0], result[1], ra, de
    def __init__(self, orbit="circular", ld="quad", collCheck=True):
        if not orbit in ["circular", "keplerian"]:
            raise (PE.PyAValError(
                "Invalid option for orbit: " + str(orbit),
                soltuion="Use either 'circular' or 'keplerian'."))
        if not ld in ["quad", "nl"]:
            raise (PE.PyAValError("Invalid option for orbit: " + str(ld),
                                  soltuion="Use either 'quad' or 'nl'."))
        _ZList.__init__(self, orbit, collCheck)

        if (not _importOccultquad) and (ld == "quad"):
            raise (PE.PyARequiredImport(
                "Could not import required shared object library 'occultquad.so'",
                solution=[
                    "Use 'pip install PyAstronomy_ext' to get it.",
                    "Invoke PyA's install script (setup.py) with the --with-ext option.",
                    "Go to 'forTrans' directory of PyAstronomy and invoke\n    f2py -c occultquad.pyf occultquad.f"
                ]))

        if (not _importOccultnl) and (ld == "nl"):
            raise (PE.PyARequiredImport(
                "Could not import required shared object library 'occultquad.so'",
                solution=[
                    "Use 'pip install PyAstronomy_ext' to get it.",
                    "Invoke PyA's install script (setup.py) with the --with-ext option.",
                    "Go to 'forTrans' directory of PyAstronomy and invoke\n    f2py -c occultnl.pyf occultnl.f"
                ]))

        if orbit == "circular":
            plist = ["p", "a", "i", "T0", "per", "b"]
        else:
            # It is an elliptical orbit
            plist = ["p", "a", "i", "per", "b", "e", "Omega", "tau", "w"]

        if ld == "quad":
            plist.extend(["linLimb", "quadLimb"])
        else:
            # Non-linear LD
            plist.extend(["a1", "a2", "a3", "a4"])

        fuf.OneDFit.__init__(self, plist)
        self.freeze(plist)
        self.setRootName("Occultquad")

        if orbit == "keplerian":
            self["w"] = -90.0

        self._orbit = orbit
        self._ld = ld
Exemple #16
0
 def pattern(self, name, form='array', key="symbol"):
   """
     Get the elemental abundance pattern
   
     Parameters
     ----------
     name : string
         The abbreviation of the abundance pattern.
     form : string, {array, dict}, optional
         Return the abundances as a plain array or as a dictionary.
         The default is 'array'.
     key : string, {symbol, number}, optional
         If return type is a dictionary, this parameter determined
         whether the elemental symbol or the atomic number is used
         as the key. The default is "symbol".
     
     Returns
     -------
     abundances : numpy array or dict (depending on ``form'')
         Number abundances (not mass) relative to H.
   """
   names = self.availablePatterns()
   if not name in names:
     raise(PE.PyAValError("No such pattern: '" + str(name) + "'", \
         solution="Choose existing pattern: " + ', '.join(names), \
         where="pyasl.AbundancePatterns"))
   if not form in ["dict", "array"]:
     raise(PE.PyAValError("No such format: '" + str(form) + "'", \
         solution="Choose either 'dict' or 'array'", \
         where="pyasl.AbundancePatterns"))
   if not key in ["symbol", "number"]:
     raise(PE.PyAValError("No such key: '" + str(key) + "'", \
         solution="Choose either 'symbol' or 'number'", \
         where="pyasl.AbundancePatterns"))      
   
   gi = np.where(names == name)[0]
   # Read abundances
   dd = np.genfromtxt(self._dataFN(), skip_header=1, usecols=gi+1)
   # Read element symbols
   el = np.genfromtxt(self._dataFN(), skip_header=1, usecols=0, dtype=str)
   if form == 'array':
     return dd
   if form == 'dict':
     if key == "symbol":
       r = {k: v for (k, v) in zip(el, dd)}
     elif key == "number":
       r = {self._an.getAtomicNo(k): v for (k, v) in zip(el, dd)}
   return r
Exemple #17
0
 def getFIP(self, atom):
   """
     Get the first ionization energy.
     
     Parameters
     ----------
     atom : string or integer
         Either a string specifying the elemental symbol (e.g., 'H') or
         an integer specifying the atomic number.
     
     Returns
     -------
     FIP : float
         First ionization potential [eV].
     FIP error : float
         Error of the FIP [eV]. May also be None
         if no error is available.
   """
   if isinstance(atom, six.string_types):
     an = self._an.getAtomicNo(atom)
   elif isinstance(atom, int):
     an = atom
   else:
     raise(PE.PyAValError("'atom' must be an integer specifying the atomic number are a string holding the elemental symbol.", \
                          where="FirstIonizationPot::getFIP"))
   return self._fip[an][0], self._fip[an][1]
Exemple #18
0
 def _plotPointAs(self, state, point=None, lbString=None):
     """
   Plot a point in active/inactive color
   
   Parameters
   ----------
   state : string, {"active", "inactive"}
       Which color to use
   point : Point, optional
       The point information as an instance of the Point class.
   lbString : string
       The point identifier used in the listbox.
 """
     if (point is None) == (lbString is None):
         raise (PE.PyAValError(
             "Either `point` or `lbString` have to be specified."))
     if point is not None:
         pli, lli = self._searchPoint(point.lbIdent)
     else:
         pli, lli = self._searchPoint(lbString)
     # Remove 'old' point from plot
     self.a.lines.pop(lli)
     if state == "active":
         style = self.astyle
     elif state == "inactive":
         style = self.istyle
     # Add new point in specified color
     self.a.plot([self.pointList[pli].xdata], [self.pointList[pli].ydata],
                 style)
     self.pointList[pli].mplLine = self.a.lines[-1]
    def getModel(self, teff, logg, met=None):
        """
        Get a model.

        Parameters
        ----------
        teff : float
            Effective temperatures [K]
        logg : float
            Logg [cgs]
        met : float, optional
            Logarithmic metallicity. If not given, the metallicity
            of the model grid is used.

        Returns
        -------
        Model : list of strings
            The model as found on the file.
        """
        if not self.modelAvailable(teff, logg, met):
            raise (PE.PyAValError((
                "No model for the combination: Teff = %6.3e, logg = %6.3e, met = "
                + str(met)) % (teff, logg)))
        if met is None:
            met = self.met
        return self.models[(teff, logg, met)][::]
Exemple #20
0
def getCardinalPoint(azimuth):
    """
    Get the cardinal point (North, East, South, or West) for a given azimuth.
    
    Here, the azimuth is measured from North to East.
    N = (315, 45] deg
    E = (45, 135] deg
    S = (135, 225] deg
    W = (225, 315] deg

    Parameters
    ----------
    azimuth : float
        Azimuth of an object in deg (0-360).

    Returns
    -------
    Cardinal point : string, {N,E,S,W}
        Returns the cardinal point (N, E, S, W) corresponding
        to the given azimuth of the object.
  """

    if (azimuth < 0.0) or (azimuth > 360.):
        raise(PE.PyAValError("The azimuth needs to be a number in the range [0,360]. Given azimuth: " + str(azimuth), \
              where="getCardinalPoint", \
              solution="Specify a number between 0 and 360"))

    if np.logical_and(azimuth > 315., azimuth <= 360.): return "N"
    if np.logical_and(azimuth >= 0., azimuth <= 45.): return "N"
    if np.logical_and(azimuth > 45., azimuth <= 135.): return "E"
    if np.logical_and(azimuth > 135., azimuth <= 225.): return "S"
    if np.logical_and(azimuth > 225., azimuth <= 315.): return "W"
    def _abundToStr(self, met):
        """
        Convert metallicity into a grid-naming compatible string

        Parameters
        ----------
        met : float
            Log10 of metallicity

        Returns
        -------
        Naming string : string
            E.g., M01, P00 etc.
        """
        if met < 0:
            result = "M"
        else:
            result = "P"
        if (met * 10) - int(abs(met) * 10) > 1e-14:
            raise (PE.PyAValError("Inappropriate met value: " + str(met)))
        met = int(abs(met) * 10)
        if met < 10:
            result += "0"
        result += str(met)
        return result
Exemple #22
0
 def evalComponent(self, x, p):
     """
   Evaluate the model considering only a single component.
   
   Parameters
   ----------
   x : array
       The abscissa.
   p : int
       Component number (starts with one).
   
   Returns
   -------
   single component model : array
       The model considering a single component.
 """
     if p > 0 and p <= self.n:
         p = str(p)
         y = self["off"] + self["lin"] * x
         y += self["A"+p] / numpy.sqrt(2.0*numpy.pi*self["sig"+p]**2) * \
              numpy.exp(-(self["mu"+p] - x)**2/(2.0 * self["sig"+p]**2))
         return y
     else:
         raise(PE.PyAValError("No such component (no. "+str(p)+")", where="MultiGauss1d::evalComponent", \
               solution="Use value between 1 and "+str(self.n)))
def ffmodelExplorer(odf, plotter, version="list"):
  """
    Instantiate the model explorer.
    
    Parameters
    ----------
    odf : Instance of OneDFit
        The model to be adapted.
    plotter : Instance of FFModelPlotFit or custom
        Class instance managing the actual plotting.
    version : string, {list}
        The version of model explorer. Currently, only
        'list' is supported.
    
    Returns
    -------
    ffmod : Model explorer
        An instance of the model explorer.
  """
  if version == "list":
    ffmod = FFModelExplorerList(odf, plotter)
    return ffmod
  elif version == "dropdown":
    PE.warn(PE.PyADeprecationError("Please note that the dropdown version is no longer supported. " + \
                                   "The list version will be called instead."))
    ffmod = FFModelExplorerList(odf, plotter)
    return ffmod
  else:
    raise(PE.PyAValError("Unknown version: '" + str(version) + "'", \
                         where="ffmodelExplorer", \
                         solution="Choose between 'list' and 'dropdown'."))
Exemple #24
0
    def xyzNodes_LOSZ(self, los="+z"):
        """
        Calculate the nodes of the orbit for LOS in +/-z direction.

        The nodes of the orbit are the points at which
        the orbit cuts the plane of the sky. In this case,
        these are the points at which the z-coordinate
        vanishes, i.e., the x-y plane is regarded the plane
        of the sky.

        Parameters
        ----------
        los : string, {+z,-z}, optional
            Line of sight points either in +z direction (observer
            at -z) or vice versa. Changing the direction
            interchanges the ascending and descending node.

        Returns
        -------
        Nodes : Tuple of two coordinate arrays
            Returns the xyz coordinates of both nodes. The first is the
            ascending node and the second is the descending node.
        """
        # f = -w (z-component vanishes there)
        E = arctan(tan(-self._w / 2.0) * sqrt(
            (1. - self.e) / (1. + self.e))) * 2.
        M = E - self.e * sin(E)
        t = M / self._n + self.tau
        node1 = self.xyzPos(t)
        # Velocity is used to distinguish nodes
        v1 = self.xyzVel(t)
        # f = -w + pi
        E = arctan(
            tan((-self._w + pi) / 2.0) * sqrt(
                (1. - self.e) / (1. + self.e))) * 2.
        M = E - self.e * sin(E)
        t = M / self._n + self.tau
        node2 = self.xyzPos(t)
        # Find the ascending and descending node
        from PyAstronomy.pyasl import LineOfSight
        l = LineOfSight(los).los
        if abs(l[2]) != 1.0:
            raise (PE.PyAValError("Invalid line of sight (LOS): " + str(los),
                                  where="xyzNodes_LOSZ",
                                  solution="Use '-z' or '+z'."))

        if l[2] == 1.0:
            # Looking in +z direction
            if v1[2] > 0.0:
                # First node is ascending
                return (node1, node2)
            else:
                return (node2, node1)
        else:
            # Looking in -z direction
            if v1[2] < 0.0:
                # First node is ascending
                return (node1, node2)
            else:
                return (node2, node1)
Exemple #25
0
    def evalComponent(self, x, p):
        """
        Evaluate the model considering only a single component.

        Parameters
        ----------
        x : array
            The abscissa.
        p : int
            Component number (starts with one).

        Returns
        -------
        Single component model : array
            The model considering a single component. Note that the
            linear continuum is included.
        """
        if p > 0 and p <= self.n:
            p = str(p)
            y = self["off"] + self["lin"] * x
            self._v1d.assignValues({
                "A": self["A" + p],
                "al": self["al" + p],
                "ad": self["ad" + p],
                "mu": self["mu" + p]
            })
            y += self._v1d.evaluate(x)
            return y
        else:
            raise (PE.PyAValError("No such component (no. " + str(p) + ")",
                                  where="MultiVoigt1d::evalComponent",
                                  solution="Use value between 1 and " +
                                  str(self.n)))
Exemple #26
0
 def _findRow(self, tab, val, valname):
     """
   Find corrected row in table.
   
   Parameters
   ----------
   tab : array
       The data table, i.e., Table 3 or 4 from
       Pizzolato et al. 2003.
   val : float
       Value to be searched for.
   valname : string
       The name of the parameter. Needed in
       case of error.
   
   Returns
   -------
   row : int
       The index of the correct row.
 """
     row = np.where((val >= tab[::, 0]) & (val < tab[::, 1]))[0]
     if len(row) == 0:
         conc = np.concatenate((tab[::, 0], tab[::, 1]))
         raise (PE.PyAValError(valname + " value of " + str(val) +
                               " is out of range. " + "Allowed range: " +
                               str(np.min(conc)) + " - " +
                               str(np.max(conc))))
     return row[0]
    def evaluate(self, t):
        if self["mstar"] == 0.0:
            raise(PE.PyAValError("Stellar mass has to be specified before evaluation.", \
                                 where="KeplerRVModel"))

        # Take into account polynomial
        for i in range(self._deg + 1):
            p = "c" + str(i)
            self.poly[p] = self[p]
        rvnew = self.poly.evaluate(t)

        for m in range(1, self._mp + 1):
            # Name add
            nadd = str(m)
            if self["per" + nadd] == 0.0:
                continue
            for p in ["per", "e", "tau", "w"]:
                # Collect parameters from current model
                self.kems[m - 1][p] = self[p + nadd]

                rvmodel = self.kems[m - 1].evaluate(t)
            # Prevent dividing by zero
            imax = np.argmax(np.abs(rvmodel))
            cee = np.cos(self.kems[m-1].ke.trueAnomaly(t[imax]) + self._dtr(self.kems[m-1]["w"])) \
                  + self["e"+nadd]*np.cos(self._dtr(self["w"+nadd]))
            rvnew += rvmodel / rvmodel[imax] * cee * self["K" + nadd]

            self["msini" + nadd] = self._getmsini(nadd)
            self["MA" + nadd] = self._MA(m)
            self["a" + nadd] = self._geta(nadd)

        return rvnew
def restoreFromFits(filename, ext=1, verbose=1):
    if not ic.check["pyfits"]:
        raise (PE.PyARequiredImport("pyfits required to use fits file.",
                                    where="Params::updateFitsHeader"))
        return
    import pyfits
    if not os.path.isfile(filename):
        raise (PE.PyAValError("No such file: " + str(filename),
                              where="restoreFromFits",
                              solution="Give correct filename."))
        return
    ff = pyfits.open(filename)[ext]
    try:
        modelname = ff.header['PA_model']
    except:
        raise (PE.pyaOtherErrors("File (" + str(filename) +
                                 ") contains no model.",
                                 where="restoreFromFits",
                                 solution="Use another file."))
        return
    if modelname == 'convLine':
        import convLine as cl
        nLines = ff.header['model_p0']
        model = cl.ConvLine(nLines)
    elif modelname == 'multiGauss1d':
        from PyAstronomy.funcFit import MultiGauss1d
        nLines = ff.header['model_p0']
        model = MultiGauss1d(nLines)
    for p in model.parameters():
        model[p] = ff.header[p]
    return model
Exemple #29
0
        def evaluate(self, x):
            """
            Calculate the model.

            Parameters
            ----------
            x : array
                The abscissa values.

            Returns
            -------
            model : array,
                The binned model.

            Notes
            -----
            This function calculates the model at those time points
            specified by the `rebinTimes` property and saves the result in the
            class property `unbinnedModel`. Then it bins
            according to the definitions in `rebinIdent` and save the resulting model
            in the `binnedModel` property.
            """
            if (self.rebinTimes is None) or (self.rebinIdent is None):
                raise(PE.PyAValError("Rebinning parameters (properties rebinTimes and/or rebinIdent) not appropriately defined.",
                                     solution=["Use setRebinArray_Ndt method.", "Define the properties by accessing them directly."]))
            # Calculate the model using current parameters at the time points
            # defined in the `rebinTimes` array
            self.unbinnedModel = CO.evaluate(self, self.rebinTimes)
            # Build up rebinned model
            self.binnedModel = numpy.zeros(x.size)
            for i in smo.range(x.size):
                self.binnedModel[i] = numpy.mean(
                    self.unbinnedModel[self.rebinIdent[i]])
            # Return the resulting model
            return self.binnedModel
Exemple #30
0
 def decompose(self, template, bn):
     """
   Carry out the SVD of the "design matrix".
   
   This method creates the "design matrix" by applying
   a bin-wise shift to the template and uses numpy's
   `svd` algorithm to carry out the decomposition.
   
   The design matrix, `des` is written in the form:
   "`des` = u * w * transpose(v)". The matrices des,
   w, and u are stored in homonymous attributes.
   
   Parameters
   ----------
   template : array or matrix
       The template with respect to which the broadening
       function shall be calculated.
   bn : int
       Width (number of elements) of the broadening function.
       Needs to be odd.
 """
     if bn % 2 != 1:
         raise (PE.PyAValError("`bn` needs to be an odd number."))
     self.bn = bn
     self.template = template.copy()
     self._createDesignMatrix(template, bn)
     # Get SVD deconvolution of the design matrix
     # Note that the `svd` method of numpy returns
     # v.H instead of v.
     self.u, self.w, v = np.linalg.svd(self.des, full_matrices=False)
     self.v = v.T