コード例 #1
0
ファイル: planck.py プロジェクト: yogeshchandola/PyAstronomy
def planck(T, lam=None, nu=None):
    """
    Evaluate Planck's radiation law.
    
    Depending on whether wavelength or frequency is specified as input, the
    function evaluates:
    
    .. math::
    
        B_{\\nu} = \\frac{2\\pi h \\nu^3}{c^2} \\frac{1}{e^{\\frac{h\\nu}{kT}} - 1}
    
    or
    
    .. math::
    
        B_{\\lambda} = \\frac{2\\pi h c^2}{\\lambda^5} \\frac{1}{e^{\\frac{h c}{\\lambda kT}} - 1} \\; .
    
    If lambda is given (in meters), the output units are W/(m^2 m). To convert into erg/(cm^2 A s),
    the output has to be multiplied by a factor of 1e-7.
    
    Parameters
    ----------
    T : float
        Temperature in Kelvin.
    lam : float or array, optional
        Wavelength in meters.
    nu : float or array, optional
        Frequency in Hz.
    
    Returns
    -------
    Spectral radiance : float or array
        Depending on whether `lam` or `nu` were specified, returns the
        spectral radiance per area and wavelength or frequency. The unit (SI)
        will be W/(m^2 m) if `lam` was given and W/(m^2 Hz) if `nu` was
        specified.
  """
    if _ic.check["quantities"]:
        from PyAstronomy import constants
        c = constants.PyAConstants(unitSystem="SI")
    else:
        raise (PE.PyARequiredImport(
            "You need to install 'quantities' to use this function.\n 'quantities' can be obtained from 'http://pypi.python.org/pypi/quantities'."
        ))
        return None
    if (lam is not None) and (nu is not None):
        raise (PE.PyAParameterConflict(
            "Specify either 'lam' OR 'nu', but not both."))
    if (lam is None) and (nu is None):
        raise (PE.PyAParameterConflict("Specify either 'lam' OR 'nu'."))
    # Parameters have been specified properly
    if lam is not None:
        result = 2. * np.pi * c.h * (c.c**2) / (lam**5)
        result /= (np.exp(c.h * c.c / (lam * c.k * T)) - 1.0)
        return result
    elif nu is not None:
        result = 2. * np.pi * c.h * (nu**3) / (c.c**2)
        result /= (np.exp(c.h * nu / (c.k * T)) - 1.0)
        return result
コード例 #2
0
ファイル: params.py プロジェクト: pcschneider/PyAstronomy
 def thaw(self, name):
     """
   Thaw (regard as free) a parameter.
   
   Parameters
   ----------
   name : string or list of strings
       The name(s) of the parameter(s) to be thawed.
 """
     names = self.__toList(name)
     for n in names:
         # Check whether the parameter is a dependent variable in
         # a relation
         for pn, rels in six.iteritems(self.relations):
             for rel in rels:
                 if rel[0] == n:
                     raise(PE.PyAParameterConflict("You tried to free the parameter '" + n + \
                                                   "', which is a dependent variable in a relation.", \
                                                   solution="Use 'untie' first to remove the relation."))
         self.__checkForParam(n)
         self.isFree[n] = True
コード例 #3
0
def transitTimes(tmin, tmax, planetData, obsOffset=0., hjd=True,
                 observatory=None, lon=None, lat=None, alt=None, minAltitude=None,
                 showTwilight="all", moonDist=None, nexaInput=False, fileOutput=None):
    """
    Calculate transit times for a given planet and a given period of time.

    The `planetData` dictionary must contain the following information:

    =======  =================================
    Key      Value
    -------  ---------------------------------
    ra       Right ascension of object [deg]
    dec      Declination of object [deg]
    T0       Time reference point (HJD)
    orbPer   Orbital period [d]
    orbInc   Orbital inclination [deg]
    SMA      Semi-major axis [AU]
    RpJ      Planetary radius [Jovian radii]
    RsSun    Stellar Radius [solar]
    Tdur     OPTIONAL, Transit duration [d]
    =======  =================================

    If the transit duration (Tdur) is not given, the duration will
    be estimated using pyasl's `transitDuration` function. 

    .. note:: The input times (`tmin` and `tmax`) are expected in JD (UT).
              Input time will be calculated to HJD.
              Time output is in HJD.

    Parameters
    ----------
    tmin : float
        Start of time interval in Julian days (UT).
    tmax : float
        End of time interval in Julian days (UT).
    planetData: dictionary
        A dictionary containing the parameters of the exoplanet
        for which the transit times should be calculated.
        The required keys are specified above.
    obs_offset : float, optional
        Specifies additional time before AND after the transit in DAYS.
        This is useful if the observation should start and end
        some time before and after the actual transit.
    hjd : boolean, optional
        If True (default), the given Julian dates specifying the time
        interval (`tmin` and `tmax`) are automatically
        converted into the heliocentric frame (HJD).
    observatory : string, optional
        If given, pyasl's `observatory` function will be used to automatically
        resolve the name and obtain longitude, latitude, and altitude
        of the observatory.
        If `observatory` is given, `lon`, `lat`, and `alt` must not be specified.
    lon : float, optional
        East longitude of the observatory given in DEGREES.

        Longitude is positive in EASTWARD direction.
        If LON is not given, transitTimes will only return beginning and end
        of the observation and the time of mid transit.
    lat : float, optional
        Latitude of the observatory given in DEGREES
        (positive in NORTHWARD direction).
    alt : float, optional
        Altitude of the observatory given in METER.
    minAltitude : float, optional
        Minimum altitude of the object in DEGREES.

        If a minimum altitude is given, only transits for which the
        object is above the given altitude during the ENTIRE
        observation are included in the list
        created by `transitTimes`. Note that `minAltitude` can
        only be used if the observer's location has been specified
        either via `observatory` or via `lon`, `lat`, and `alt`.
    showTwilight : string, optional, {"all", "civil", "nautical", "astronomical", "night"}
        Specifies the twilight acceptable during the observation.
        By default all twilight conditions are acceptable.

        Only the transits for which the ENTIRE observation
        occurs during the specified or darker twilight conditions
        are listed.

        The choices are:
          - "all": all transits are shown (even during day)
          - "civil": only transits during civil twilight and better are shown
          - "nautical": only transits during nautical twilight and better are shown
          - "astronomical": only transits during astronomical twilight and better are shown
          - "night": only transits during night are shown

        Note that this can only have an effect, if the observer's location is
        specified.
    moonDist : float
        Minimum distance between the Moon and the target in DEGREES.
        By default all Moon distances are acceptable (moonDist=0.0).

        Only observations are listed for which the angular distance between
        the Moon and the target is larger
        than `moonDist` during the ENTIRE observation.

        Note that this can only have an effect, if the observer's location is
        specified.
    fileOutput : string or file, optional
        If a string is given, a file with the name will be created
        and the output will be written to that file. If a (writable)
        file object is given, the output will be written to that
        file. In both cases, no output will be given on screen. 

    Returns
    -------
    Transit times : dictionary
        Returns a dictionary containing the transit details. The dictionary key
        is a running number (starting with one), which is equivalent to that 
        listed in the first column of the table.

        For each transit, the function returns a dictionary with the transit
        details.

        If the observer's location was not specified, the dictionary has the
        following keys:

          ============    ====================================================
          Key             Description
          ------------    ----------------------------------------------------
          Planet name     Name of the planet
          Tmid            HJD of transit center
          Transit jd      Array giving JD of start, mid-time, and end of
                          transit.
          Obs jd          Array specifying the HJD of the start, center and
                          end of the observation.
          Obs cal         Equivalent to 'Obs jd', but in the form of the
                          calendar date. In particular, for each date, a list
                          containing [Year, month, day, fractional hours]
                          is given.

                          **Below follows optional output only present**
                          **if the observer's location is known**

          Obs coord       East longitude [deg], latitude [deg], and
                          altitude [m] of the observatory.
          Sun ra          Right ascension of the Sun at center of
                          observation.
          Sun dec         Declination of the Sun at center of
                          observation.
          Sun alt         Altitude of the Sun [deg] at begin, center, and
                          end of the observation.
          Sun az          Azimuth if the Sun [deg] at begin, center, and
                          end of the observation.
          Moon phase      Array giving lunar phase (in percent) at start,
                          center, and end of the observation.
          Moon AD         Angular distance between the target and the Moon
                          at begin, center, and end of the observation [deg].
          Moon ra         Right ascension of the Moon at begin, center, and
                          end of the observation [deg].
          Moon dec        Declination of the Moon at begin, center, and
                          end of the observation [deg].
          Star ra         Right ascension of the star [deg].
          Star dec        Declination of the star [deg].
          Star CP         Cardinal point of the star at begin, center, and
                          end of the observation.
          Star alt        Altitude of the star [deg] at begin, center, and
                          end of the observation.
          Star az         Azimuth of the star [deg] at begin, center, and
                          end of the observation.
          Twilight        The worst, i.e., brightest type of twilight
                          encountered during the observation.
          ============    ====================================================

    """

    if fileOutput is not None:
        oldStdout = sys.stdout
        if isinstance(fileOutput, six.string_types):
            sys.stdout = open(fileOutput, 'w')
        else:
            sys.stdout = fileOutput

    try:

        if tmin >= tmax:
            raise(PE.PyAValError("The given time range is inconsistent (tmin >= tmax)",
                                 where="transitTimes",
                                 solution="Adapt tmin and tmax."))

        # Copy input dictionary, because it may be changed
        planetData = planetData.copy()

        if nexaInput:
            pdin = planetData.copy()
            planetData = {}
            planetData["ra"] = pdin["ra"]
            planetData["dec"] = pdin["dec"]
            planetData["orbPer"] = pdin["pl_orbper"]
            planetData["T0"] = pdin["pl_tranmid"]
            planetData["orbInc"] = pdin["pl_orbincl"]
            planetData["SMA"] = pdin["pl_orbsmax"]
            planetData["RpJ"] = pdin["pl_radj"]
            planetData["RsSun"] = pdin["st_rad"]
            planetData["Tdur"] = pdin["pl_trandur"]
            planetData["plName"] = pdin["pl_name"]
            if np.isnan(planetData["Tdur"]):
                del planetData["Tdur"]

        # Check whether required keys are present
        reke = ["ra", "dec", "orbPer", "T0",
                "orbInc", "SMA", "RpJ", "RsSun", "plName"]
        msg = ""
        fail = False
        for key in reke:
            if (not key in planetData):
                msg += "The required key '" + key + "' is missing in the input data!\n"
                fail = True
                continue
            if isinstance(planetData[key], (tuple(six.integer_types) + (float,))):
                if np.isnan(planetData[key]):
                    msg += "The required key '" + key + "' has NaN value in the input data!\n"
                    fail = True
        if fail:
            raise(PE.PyAValError("The input `planetData` is inappropriate:\n" + msg,
                                 where="transitTimes",
                                 solution="Specify all required input values."))

        # Object position [degrees]
        ra = planetData["ra"]
        dec = planetData["dec"]

        if hjd:
            # Convert input times into heliocentric frame
            # Using 'reduced' JD in calculation
            tmin = helio_jd(tmin - 2.4e6, ra, dec) + 2.4e6
            tmax = helio_jd(tmax - 2.4e6, ra, dec) + 2.4e6

        print("Specified time span")
        print(
            "Start date (DDDD-MM-YY and fractional hours): {0:4d}-{1:02d}-{2:02d} {3:6.3f}".format(*daycnv(tmin)))
        print(
            "End date (DDDD-MM-YY and fractional hours): {0:4d}-{1:02d}-{2:02d} {3:6.3f}".format(*daycnv(tmax)))
        print()

        # Transit parameters
        # Orbital period in days
        period = planetData["orbPer"]
        # Transit reference time (should be HJD)
        T0 = planetData["T0"]

        if not "Tdur" in planetData:
            # No duration specified in the data
            inc = planetData["orbInc"]  # deg
            sma = planetData["SMA"]  # au
            rp = planetData["RpJ"]  # Mjup
            rs = planetData["RsSun"]  # Msun
            dur = transitDuration(sma, rp, rs, inc, period)
            print(
                "Estimating transit duration using orbital inclination, semi-major axis,")
            print("  planetary radius, and stellar radius")
        else:
            dur = planetData["Tdur"]

        print("Transit duration: ", dur * 24. * 60., " minutes")
        print("Off-transit time before and after transit: ",
              obsOffset * 24. * 60., " minutes")

        # First and last epoch contained in specified range
        trnum_start = np.floor((tmin - T0) / period)
        trnum_end = np.ceil((tmax - T0) / period)
        # Relevant transit epochs
        tr = np.arange(trnum_start, trnum_end, 1)

        if (observatory is not None) and \
                ((lon is not None) or (lat is not None) or (alt is not None)):
            raise(PE.PyAParameterConflict("You must either specify `observatory` OR `lon`, `lat`, and `alt`.",
                                          where="transitTimes",
                                          solution="Adapt function call."))

        if observatory is not None:
            # Resolve observatory string
            observatory_data = pyaobs.observatory(observatory)
            lon = observatory_data["longitude"]
            lat = observatory_data["latitude"]
            alt = observatory_data["altitude"]

        # Check if the observatory data are complete
        obsCompl = (lon is None) + (lat is None) + (alt is None)
        if (obsCompl == 1) or (obsCompl == 2):
            raise(PE.PyAValError("Observatory data is incomplete. `lon`, `lat`, and `alt` must all be specified.\n" +
                                 "Current values are: lon = " +
                                 str(lon) + ", lat = " + str(lat) +
                                 ", alt = " + str(alt),
                                 where="transitTimes",
                                 solution="Provide complete observatory information."))

        if (minAltitude is not None) and (lon is None):
            # Observer's location not given so minAltitude cannot have any effect
            raise(PE.PyAParameterConflict("The observer's location is not specified, but `minAltitude` is given.\n" +
                                          "This parameter can only be used, if the observer's location is known.",
                                          where="transitTimes",
                                          solution="Either specify the observer's location or set `minAltitude` to None."))

        if (showTwilight != "all") and (lon is None):
            # Observer's location not given so showTwilight cannot have any effect
            raise(PE.PyAParameterConflict("The observer's location is not specified, but `showTwilight` is given.\n" +
                                          "This parameter can only be used, if the observer's location is known.",
                                          where="transitTimes",
                                          solution="Either specify the observer's location or set `showTwilight` to \"all\"."))

        if (showTwilight != "all") and (showTwilight != "civil") and (showTwilight != "nautical") and \
           (showTwilight != "astronomical") and (showTwilight != "night"):
            # None of the possible choices for showTwilight have been used.
            raise(PE.PyAValError("Wrong keyword given for showTwilight.\n" +
                                 "Current keyword is " + showTwilight,
                                 where="transitTimes",
                                 solution="Select a valid keyword for showTwilight: `all', `civil', `nautical', `astronomical', or `night'."))

        if moonDist is None:
            # No limit on Moon distance
            moonDist = 0.0
        if (moonDist != 0.0) and (lon is None):
            # Observer's location not given so showTwilight cannot have any effect
            raise(PE.PyAParameterConflict("The observer's location is not specified, but `moonDist` is given.\n" +
                                          "This parameter can only be used, if the observer's location is known.",
                                          where="transitTimes",
                                          solution="Either specify the observer's location or set `moonDist` to 0.0 or None."))

        if moonDist < 0.0:
            # Moon distance below zero does not make sense
            PE.warn("The specified `moonDist' is below zero (" + str(moonDist) + ") which does not make sense.\n" +
                    "It was changed to 0.0.\n" +
                    "Please use a value >= 0.0 or None if specifying `moonDist'.")

        print()
        if np.logical_and(lon != None, lat != None):
            print("No. Tmid [HJD]      Obs. start [UT] [ALT, DIR(AZI)]     Transit mid [UT] [ALT, DIR(AZI)]     Obs. end [UT] [ALT, DIR(AZI)]   twilight" +
                  " (SUN ALT)                   moon distance     moon phase")
        else:
            print(
                "No. Tmid [HJD]      Obs. start [UT]    Transit mid [UT]   Obs. end [UT]")

        allData = {}
        trcounter = 1
        for i in tr:
            trData = {}
            # Get times
            Tmid = T0 + float(i) * period
            if (Tmid < tmin) or (Tmid > tmax):
                # This may happen because the transit may occur in the first
                # relevant epoch but still before tmin. Likewise for tmax.
                continue
            obs_start_hjd = Tmid - (dur / 2.0) - obsOffset
            obs_start = daycnv(obs_start_hjd)
            obs_mid = daycnv(Tmid)
            obs_end_hjd = Tmid + (dur / 2.0) + obsOffset
            obs_end = daycnv(obs_end_hjd)
            time_temp = np.array([obs_start_hjd, Tmid, obs_end_hjd])
            transit_only = np.array(
                [Tmid - (dur / 2.0), Tmid, Tmid + (dur / 2.0)])

            # Get visibility
            if (lon is not None) and (lat is not None):
                # Get alt/az of object for current transit
                altaz = eq2hor.eq2hor(time_temp, np.ones(time_temp.size) * ra,
                                      np.ones(time_temp.size) * dec, lon=lon, lat=lat, alt=alt)
                # If minimum altitude is not fulfilled during observation,
                # do not show transit
                if minAltitude is not None:
                    minalt = np.where(altaz[0] >= minAltitude)[0]
                    if len(minalt) < time_temp.size:
                        # Skip this transit
                        continue
                # Get Sun position for current transit
                sunpos_radec = sunpos.sunpos(time_temp[1])
                sunpos_altaz = eq2hor.eq2hor(time_temp, np.ones(time_temp.size) * sunpos_radec[1],
                                             np.ones(time_temp.size) *
                                             sunpos_radec[2],
                                             lon=lon, lat=lat, alt=alt)
                twi = twilight.twilightName(max(sunpos_altaz[0]))
                # Check type of twilight -> if requirement not fulfilled, don't show transit
                if showTwilight == "civil":
                    # Show civil or better
                    if twi == "day":
                        continue
                if showTwilight == "nautical":
                    # Show nautical or better
                    if twi == "day":
                        continue
                    if twi == "civil twilight":
                        continue
                if showTwilight == "astronomical":
                    # Show astronomical or better
                    if twi == "day":
                        continue
                    if twi == "civil twilight":
                        continue
                    if twi == "nautical twilight":
                        continue
                if showTwilight == "night":
                    # Only show night
                    if twi != "night":
                        continue

                # Get Moon position for current transit
                mpos = moonpos(time_temp)
                mdists = []
                for i in range(time_temp.size):
                    mdists.append(getAngDist(mpos[0][i], mpos[1][i], ra, dec))
                mdist = min(mdists)
                # Check Moon distance, if not fulfilled, neglect the transit
                if mdist < moonDist:
                    continue
                # Get lunar phase in percent
                moonpha = moonphase(time_temp) * 100.
                print("%3d %10.5f   %2d.%2d. %2d:%02d    [%3d°,%s(%3d°)]      %2d.%2d. %2d:%02d     [%3d°,%s(%3d°)]      %2d.%2d. %2d:%02d  [%3d°,%s(%3d°)]   %18s (%3d°,%3d°,%3d°)   (%3d°,%3d°,%3d°)  %3d%%"
                      % (trcounter, Tmid, obs_start[2], obs_start[1], np.floor(obs_start[3]), (obs_start[3] - np.floor(obs_start[3])) * 60.,
                         altaz[0][0], getCardinalPoint(
                             altaz[1][0]), altaz[1][0],
                         obs_mid[2], obs_mid[1], np.floor(
                             obs_mid[3]), (obs_mid[3] - np.floor(obs_mid[3])) * 60.,
                         altaz[0][1], getCardinalPoint(
                             altaz[1][1]), altaz[1][1],
                         obs_end[2], obs_end[1], np.floor(
                             obs_end[3]), (obs_end[3] - np.floor(obs_end[3])) * 60.,
                         altaz[0][2], getCardinalPoint(
                             altaz[1][2]), altaz[1][2], twi, sunpos_altaz[0][0], sunpos_altaz[0][1], sunpos_altaz[0][2],
                         mdists[0], mdists[1], mdists[2], np.max(moonpha)))
                # Save transit data
                trData["Tmid"] = Tmid
                trData["Obs jd"] = time_temp
                trData["Obs cal"] = [obs_start, obs_mid, obs_end]
                trData["Star ra"] = ra
                trData["Star dec"] = dec
                trData["Star alt"] = altaz[0]
                trData["Star az"] = altaz[1]
                trData["Sun ra"] = sunpos_radec[1]
                trData["Sun dec"] = sunpos_radec[2]
                trData["Sun alt"] = sunpos_altaz[0]
                trData["Sun az"] = sunpos_altaz[1]
                trData["Twilight"] = twi
                trData["Moon ra"] = mpos[0]
                trData["Moon dec"] = mpos[1]
                trData["Moon AD"] = mdist
                trData["Moon phase"] = moonpha
                trData["Star CP"] = [getCardinalPoint(altaz[1][0]), getCardinalPoint(
                    altaz[1][1]), getCardinalPoint(altaz[1][2])]

                trData["Obs coord"] = [lon, lat, alt]
            else:
                # If you do not specify the observer's location, return all transits of the object
                print("%3d %10.5f   %2d.%2d. %2d:%02d       %2d.%2d. %2d:%02d       %2d.%2d. %2d:%02d"
                      % (trcounter, Tmid, obs_start[2], obs_start[1], np.floor(obs_start[3]), (obs_start[3] - np.floor(obs_start[3])) * 60.,
                         obs_mid[2], obs_mid[1], np.floor(
                             obs_mid[3]), (obs_mid[3] - np.floor(obs_mid[3])) * 60.,
                         obs_end[2], obs_end[1], np.floor(obs_end[3]), (obs_end[3] - np.floor(obs_end[3])) * 60.))
                trData["Tmid"] = Tmid
                trData["Obs jd"] = time_temp
                trData["Obs cal"] = [obs_start, obs_mid, obs_end]

            trData["Transit jd"] = transit_only
            trData["Planet name"] = planetData["plName"]
            allData[trcounter] = trData
            trcounter += 1

        if len(allData) == 0:
            print()
            print("------------------------------------------------------")
            print("!!! No transits found for the given restrictions. !!!")
            print("------------------------------------------------------")
            print()

    except:
        raise
    finally:
        if fileOutput is not None:
            if isinstance(fileOutput, six.string_types):
                sys.stdout.close()
            sys.stdout = oldStdout

    return allData
コード例 #4
0
ファイル: binning.py プロジェクト: mark-williams/PyAstronomy
def binningx0dt(x,
                y,
                yerr=None,
                x0=None,
                dt=None,
                nbins=None,
                reduceBy=None,
                removeEmpty=True,
                removeNoError=False,
                useBinCenter=True,
                useMeanX=False,
                nanHandling=None,
                yvalFunc=np.mean):
    """
    A simple binning algorithm.

    This algorithm uses a fixed bin-width to produce a binned
    data set. Either the bin-width, `dt`, or the number of bins,
    `nbins`, must be specified. The number of output bins may
    also depend on other flags such as, for example, `removeNoError`.

    If no errors are specified via `yerr`, the errors for the binned
    data are estimated as the standard deviation of the input data
    points divided by the square root of their number. If `yerr` has
    been specified, error propagation is used to determine the error.

    The behavior of the x-axis can be controlled via the
    `useBinCenter` flag.

    Values which cannot be determined will be indicated by NaN.
    Various flags can be used to remove such bins from the binned
    data set.

    Parameters
    ----------
    x, y : array
        The x and y data values.
    yerr : array, optional
        Errors on the data values.
    x0 : float, optional
        Starting time of first bin.
        Default is lowest given x value.
    dt : float, optional
        Width of a bin (either `dt`, `nbins` or `reduceBy` must
        be given).
    nbins : int, optional
        Number of bins to use (either `dt`, `nbins` or `reduceBy` must
        be given). Note that this specifies the number of bins into which
        the range from `x0` to the last data point is subdivided.
    reduceBy : int, optional
        Reduce the number of elements in the array by the given factor 
        (either `dt`, `nbins` or `reduceBy` must be given). Note that
        in this case, `x0` is set to the first (minimum x-value) and
        the number of bins, n, is calculated according to the
        prescription: :math:`n = int(round(len(x)/reduceBy))`
    removeEmpty : boolean, optional
        If True (default), bins with no data points will be
        removed from the result.
    removeNoError : boolean, optional
        If True, bins for which no error can be determined
        will be removed from the result. Default is False.
    useBinCenter : boolean, optional
        If True (default), the time axis will refer to the
        center of the bins. Otherwise the numbers refer to
        the start of the bins.
    useMeanX : boolean, optional
        If True, the binned x-values refer to the mean x-value
        of all points in that bin.
        Therefore, the new time axis does not have to be equidistant.
    yvalFunc : callable, optional
        Function used to determine the value in a bin based on input data.
        Default is the mean value (np.mean). An alternative choice could, e.g.,
        be np.median.
    nanHandling : None, "ignore", float, (optional)
        Controls how NaNs in the data are handled.
          - None: By default (None),
            nothing is done and NaNs are treated as if they were valid
            input data, so that they are carried over into the binned data.
            This means that output bins containing NaN(s) will also end up
            as NaN(s). If 'ignore'
          - 'ignore': In this case, NaNs contained in the input data are
            removed from the data prior binning. Note however, that `x0`,
            unless specified explicitly, will still refer to the first data
            point, whether or not this holds a NaN value. 
          - float: If a float is given, input data values containing NaNs
            are replaced by the given float before binning. Note that no error on
            the data (yerr) can be considered in this case, to avoid 
            erronous treatment of un- or misspecified error values.

    Returns
    -------
    Binned data set : array
        An array with four columns: 1) The new x-axis,
        2) The binned data (the mean value of the data points located
        in the individual bins), 3) Error of binned data, 4) The
        number of input data points used to create the bin.
        For instance, the new x-values can be accessed
        using result[::,0].
    dt : float
        The width of the bins.
    """
    if len(x) != len(y):
        raise (PE.PyAValError("x and y need to have the same length."))
    if ((not dt is None) + (not nbins is None) + (not reduceBy is None)) != 1:
        raise (PE.PyAParameterConflict(
            "Specify one of `dt`, `nbins`, or `reduceBy`."))
    if ((not x0 is None) + (not reduceBy is None)) != 1:
        raise (PE.PyAParameterConflict("Specify either `x0` or `reduceBy`."))
    if x0 is None:
        # Use first time as starting point
        x0 = np.min(x)
    if x0 > np.max(x):
        raise (PE.PyAValError(
            "The starting point, `x0`, is larger than the end time of the data.",
            solution="Use a smaller value."))
    if np.any(np.isfinite(x) == False):
        raise(PE.PyAValError("X axis contains invalid values (NaN or inf).", \
                             solution="Remove invalid values from input."))

    # Use arrays in calculation. Only copy if conversion to numpy array
    # is required
    xl, yl, yerrl = False, False, False
    if not isinstance(x, np.ndarray):
        x = np.array(x)
        xl = True
    if not isinstance(y, np.ndarray):
        y = np.array(y)
        yl = True
    if not yerr is None:
        if not isinstance(yerr, np.ndarray):
            yerr = np.array(yerr)
            yerrl = True

    # nanHandling
    if nanHandling is not None:
        # As some manipulation of the data may be required, generate
        # local copies, unless this has already happened.
        if not xl:
            x = copy.copy(x)
        if not yl:
            y = copy.copy(y)
        if not yerrl:
            yerr = copy.copy(yerr)

        if nanHandling == "ignore":  # Remove bins containing NaN from the input data
            gi = np.isfinite(y)
            x, y = x[gi], y[gi]
            if not yerr is None:
                # Check if bins containing NaN from the input data
                yerr = yerr[gi]
        else:
            try:
                nanValue = float(nanHandling)
            except ValueError as ve:
                raise (PE.PyAValError(
                    "Invalid value given for 'nanHandling'. Error was: " +
                    str(ve),
                    solution="Use None, 'ignore', or float number."))

            gi = np.isnan(y)
            y[gi] = nanValue
            if yerr is not None:
                raise (PE.PyAValError(
                    "yerr is not permitted for option 'nanHandling=float'.",
                    solution="Remove yerr=... from the call."))

    # Calculate the new number of array elements.
    if reduceBy is not None:
        nbins = int(round(len(x) / float(reduceBy)))
        if nbins == 0:
            nbins = 1  # Prevent empty return arrays
    if nbins is not None:
        # Use a specified number of bins.
        # Calculate bin length
        dt = (np.max(x) - x0) / float(nbins)
    # Start calculation
    # In which bin do the individual data points belong?
    inWhichBin = np.floor(((x - x0) / dt)).astype(np.int)
    # Lonely last bin correction
    # Brings the last data point into the last valid bin
    # instead of creating a new bin with that data point\
    # at its very beginning
    if nbins is not None:
        inWhichBin[np.where(inWhichBin == nbins)[0]] -= 1
    # Get the number of bins (start at x0 even if the
    # first bins do not contain any data points)
    nbins = np.max(inWhichBin) + 1
    # Bins with data
    bwd = np.unique(inWhichBin)
    # Sort data into the bins
    # Create output array (time, flux, error, data-point-counter)
    result = np.empty((nbins, 4))
    result[:] = np.NAN
    # Assign time axis (beginning of bins)
    result[::, 0] = x0 + np.arange(nbins) * dt
    if useBinCenter:
        # Use the center of the bin for timing
        result[::, 0] += (0.5 * dt)
    # Set data point counter (points/bin) to zero
    result[::, 3] = 0
    for b in bwd:
        indi = np.where(inWhichBin == b)[0]
        result[b, 3] = len(indi)
        result[b, 1] = yvalFunc(y[indi])
        if useMeanX:
            # Overwrite the time axis using the mean x-value
            result[b, 0] = np.mean(x[indi])
        if yerr is None:
            # No errors on data points are given
            if len(indi) > 1:
                result[b, 2] = np.std(y[indi]) / np.sqrt(result[b, 3])
            else:
                # No error if there is only a single point in the bin
                result[b, 2] = np.NAN
        else:
            # There are errors on the data points
            # Use error propagation
            result[b, 2] = np.sqrt(np.sum(yerr[indi]**2)) / result[b, 3]

    if removeEmpty:
        # Remove bins without data points in it
        indi = np.where(np.invert(np.isnan(result[::, 1])))[0]
        result = result[indi, ::]
    if removeNoError:
        # Remove bins for which no error can be given
        indi = np.where(np.invert(np.isnan(result[::, 2])))[0]
        result = result[indi, ::]
    return result, dt