def velocity_in_plane(times,parameters,component='primary',coordinate_frame='polar'):
    """
    Calculate the velocity in the orbital plane.
    
    @param times: times of observations (days)
    @type times: array
    @param parameters: list of parameters (P,e,a,T0)
    @type parameters: list
    @param component: component to calculate the orbit of. If it's the secondary,
    the angles will be shifted by 180 degrees
    @type component: string, one of 'primary' or 'secondary'
    @param coordinate_frame: type of coordinates
    @type coordinate_frame: str, ('polar' or 'cartesian')
    @return: coord1,coord2
    @rtype: 2xarray
    """
    #-- calculate the orbit in the plane
    r,theta = orbit_in_plane(times,parameters,component=component,coordinate_frame='polar')
    P,e,a,T0 = parameters
    P = conversions.convert('d','s',P)
    a = conversions.convert('au','m',a)
    
    #-- compute rdot and thetadot
    l = r*(1+e*np.cos(theta))
    L = 2*np.pi*a**2/P*np.sqrt(1-e**2)
    rdot = L/l*e*np.sin(theta)
    thetadot = L/r**2
    
    #-- convert to the right coordinate frame
    if coordinate_frame=='polar':
        return rdot,thetadot
    elif coordinate_frame=='cartesian':
        vx,vy,vz = vectors.spher2cart((r,theta,np.pi/2.),(rdot,r*thetadot,0))
        return vx,vy
def radial_velocity(parameters,times=None,theta=None,itermax=8):
    """
    Evaluate Radial velocities due to kepler orbit.
    
    These parameters define the Keplerian orbit if you give times points (C{times}, days):
        1. Period of the system (days)
        2. time of periastron passage T0 (not x0!) (HJD)
        3. eccentricity
        4. omega, the longitude of periastron gives the orientation
           of the orbit within its own plane (radians)
        5. the semiamplitude of the velocity curve (km/s)
        6. systemic velocity RV0 (RV of centre of mass of system) (km/s)
    
    These parameters define the Keplerian orbit if you give angles (C{theta}, radians):
        1. Period of the system (days)
        2. eccentricity
        3. a, the semi-major axis of the absolute orbit of the component (au)
        4. omega, the longitude of periastron gives the orientation of the
        orbit within in its own plane (radians)
        5. inclination of the orbit (radians)
        6. systemic velocity RV0 (RV of centre of mass of system) (km/s)
    
    The periastron passage T0 can be derived via x0 by calculating
    
    T0 = x0/(2pi*Freq) + times[0]
    
    See e.g. p 41,42 of Hilditch, 'An Introduction To Close Binary Stars'
    
    @parameter parameters: parameters of Keplerian orbit (dependent on input)
    @type parameters: iterable
    @parameter times: observation times (days)
    @type times: numpy array
    @parameter theta: orbital angles (radians)
    @type theta: numpy array
    @param itermax: number of iterations to find true anomaly
    @type itermax: integer
    @return: fitted radial velocities (km/s)
    @rtype: ndarray
    """
    
    #-- in the case time points are given:
    if times is not None:
        #-- expand parameters and calculate x0
        P,T0,e,omega,K,RV0 = parameters
        freq = 1./P
        x0 = T0*2*np.pi*freq
        #-- calculate true anomaly
        E,true_an = true_anomaly(times*2*np.pi*freq-x0,e,itermax=itermax)
        #-- evaluate Keplerian radial velocity orbit
        RVfit = RV0 + K*(e*np.cos(omega) + np.cos(true_an+omega))
    
    elif theta is not None:
        P,e,a,omega,i,RV0 = parameters
        P = conversions.convert('d','s',P)
        a = conversions.convert('au','m',a)
        K = 2*np.pi*a*np.sin(i)/ (P*np.sqrt(1.-e**2))
        RVfit = RV0 + K*(e*np.cos(omega) + np.cos(theta+omega))/1000.
        
    return RVfit
Beispiel #3
0
def orbit_in_plane(times,
                   parameters,
                   component='primary',
                   coordinate_frame='polar'):
    """
    Construct an orbit in the orbital plane.

    Give times in days

    Parameters contains:
        1. period (days)
        2. eccentricity
        3. semi-major axis (au)
        4. time of periastron passage T0 (not x0!) (HJD)

    Return r (m) and theta (radians)

    @param times: times of observations (days)
    @type times: array
    @param parameters: list of parameters (P,e,a,T0)
    @type parameters: list
    @param component: component to calculate the orbit of. If it's the secondary,
    the angles will be shifted by 180 degrees
    @type component: string, one of 'primary' or 'secondary'
    @param coordinate_frame: type of coordinates
    @type coordinate_frame: str, ('polar' or 'cartesian')
    @return: coord1,coord2
    @rtype: 2xarray
    """
    P, e, a, T0 = parameters
    P = conversions.convert('d', 's', P)
    a = conversions.convert('au', 'm', a)
    T0 = conversions.convert('d', 's', T0)
    times = conversions.convert('d', 's', times)

    n = 2 * np.pi / P
    ma = n * (times - T0)
    E, theta = true_anomaly(ma, e)
    r = a * (1 - e * np.cos(E))
    PR = r * np.sin(theta)
    #PR[E>0] *= -1
    #theta[E>0] *= -1

    #-- correct angles if secondary component is calculated
    if 'sec' in component.lower():
        theta += np.pi

    if coordinate_frame == 'polar':
        return r, theta
    elif coordinate_frame == 'cartesian':
        return r * np.cos(theta), r * np.sin(theta)
Beispiel #4
0
def calc_gr(loggms, M1, M2, dv):

    G = constants.GG_cgs
    c = constants.cc_cgs
    Msol = constants.Msol_cgs

    gr_ms = 10**loggms / c * np.sqrt(G * M1 * Msol / 10**loggms)
    gr_ms = conversions.convert('cm s-1', 'km s-1', gr_ms)

    gr_tot = gr_ms + dv

    gr = conversions.convert('km s-1', 'cm s-1', gr_tot)
    loggsdb = np.log10(gr**2 * c**2 / (G * M2 * Msol))

    return loggsdb
def orbit_in_plane(times,parameters,component='primary',coordinate_frame='polar'):
    """
    Construct an orbit in the orbital plane.
    
    Give times in days
    
    Parameters contains:
        1. period (days)
        2. eccentricity
        3. semi-major axis (au)
        4. time of periastron passage T0 (not x0!) (HJD)
    
    Return r (m) and theta (radians)
    
    @param times: times of observations (days)
    @type times: array
    @param parameters: list of parameters (P,e,a,T0)
    @type parameters: list
    @param component: component to calculate the orbit of. If it's the secondary,
    the angles will be shifted by 180 degrees
    @type component: string, one of 'primary' or 'secondary'
    @param coordinate_frame: type of coordinates
    @type coordinate_frame: str, ('polar' or 'cartesian')
    @return: coord1,coord2
    @rtype: 2xarray
    """
    P,e,a,T0 = parameters
    P = conversions.convert('d','s',P)
    a = conversions.convert('au','m',a)
    T0 = conversions.convert('d','s',T0)
    times = conversions.convert('d','s',times)
    
    n = 2*np.pi/P
    ma = n*(times-T0)
    E,theta = true_anomaly(ma,e)
    r = a*(1-e*np.cos(E))
    PR = r*np.sin(theta)
    #PR[E>0] *= -1
    #theta[E>0] *= -1
    
    #-- correct angles if secondary component is calculated
    if 'sec' in component.lower():
        theta += np.pi
    
    if coordinate_frame=='polar':
        return r,theta
    elif coordinate_frame=='cartesian':
        return r*np.cos(theta),r*np.sin(theta)
def critical_angular_velocity(M,R_pole,units='Hz'):
    """
    Compute the critical angular velocity (Hz).
    
    Definition taken from Cranmer and Owocki, 1995 and equal to
    
    Omega_crit = sqrt( 8GM / 27Rp**3 )
    
    Example usage (includes conversion to period in days):
    
    >>> Omega = critical_angular_velocity(1.,1.)
    >>> P = 2*pi/Omega
    >>> P = conversions.convert('s','d',P)
    >>> print 'Critical rotation period of the Sun: %.3f days'%(P)
    Critical rotation period of the Sun: 0.213 days
        
    @param M: mass (solar masses)
    @type M: float
    @param R_pole: polar radius (solar radii)
    @type R_pole: float
    @param units: if you wish other units than Hz, you can give them here
    @type units: string understandable by L{<units.conversions.convert>}
    @return: critical angular velocity in Hz
    @rtype: float
    """
    M = M*constants.Msol
    R = R_pole*constants.Rsol
    omega_crit = np.sqrt( 8*constants.GG*M / (27.*R**3))
    if units.lower()!='hz':
        omega_crit = conversions.convert('Hz',units,omega_crit)
    return omega_crit
def critical_angular_velocity(M,R_pole,units='Hz'):
    """
    Compute the critical angular velocity (Hz).

    Definition taken from Cranmer and Owocki, 1995 and equal to

    Omega_crit = sqrt( 8GM / 27Rp**3 )

    Example usage (includes conversion to period in days):

    >>> Omega = critical_angular_velocity(1.,1.)
    >>> P = 2*pi/Omega
    >>> P = conversions.convert('s','d',P)
    >>> print('Critical rotation period of the Sun: %.3f days'%(P))
    Critical rotation period of the Sun: 0.213 days)

    @param M: mass (solar masses)
    @type M: float
    @param R_pole: polar radius (solar radii)
    @type R_pole: float
    @param units: if you wish other units than Hz, you can give them here
    @type units: string understandable by L{<units.conversions.convert>}
    @return: critical angular velocity in Hz
    @rtype: float
    """
    M = M*constants.Msol
    R = R_pole*constants.Rsol
    omega_crit = np.sqrt( 8*constants.GG*M / (27.*R**3))
    if units.lower()!='hz':
        omega_crit = conversions.convert('Hz',units,omega_crit)
    return omega_crit
Beispiel #8
0
def get_photometry(ID=None,extra_fields=['_r','_RAJ2000','_DEJ2000'],**kwargs):
    """
    Download all available photometry from a star to a record array.
    
    For extra kwargs, see L{_get_URI} and L{mast2phot}
    """
    to_units = kwargs.pop('to_units','erg/s/cm2/AA')
    master_ = kwargs.get('master',None)
    master = None
    #-- retrieve all measurements
    for source in cat_info.sections():
        if source=='galex':
            results,units,comms = galex(ID=ID,**kwargs)
        else:
            results,units,comms = search(source,ID=ID,**kwargs)
        
        if results is not None:
            master = mast2phot(source,results,units,master,extra_fields=extra_fields)
    
    #-- convert the measurement to a common unit.
    if to_units and master is not None:
        #-- prepare columns to extend to basic master
        dtypes = [('cwave','f8'),('cmeas','f8'),('e_cmeas','f8'),('cunit','a50')]
        cols = [[],[],[],[]]
        #-- forget about 'nan' errors for the moment
        no_errors = np.isnan(master['e_meas'])
        master['e_meas'][no_errors] = 0.
        #-- extend basic master
        zp = filters.get_info(master['photband'])
        for i in range(len(master)):
            try:
                value,e_value = conversions.convert(master['unit'][i],to_units,master['meas'][i],master['e_meas'][i],photband=master['photband'][i])
            except ValueError: # calibrations not available
                value,e_value = np.nan,np.nan
            except AssertionError: # postive flux and errors!
                value,e_value = np.nan,np.nan
            try:
                eff_wave = filters.eff_wave(master['photband'][i])
            except IOError:
                eff_wave = np.nan
            cols[0].append(eff_wave)
            cols[1].append(value)
            cols[2].append(e_value)
            cols[3].append(to_units)
        master = numpy_ext.recarr_addcols(master,cols,dtypes)
        #-- reset errors
        master['e_meas'][no_errors] = np.nan
        master['e_cmeas'][no_errors] = np.nan
    
    if master_ is not None and master is not None:
        master = numpy_ext.recarr_addrows(master_,master.tolist())
    elif master_ is not None:
        master = master_
    
    #-- and return the results
    return master    
Beispiel #9
0
def velocity_in_plane(times,
                      parameters,
                      component='primary',
                      coordinate_frame='polar'):
    """
    Calculate the velocity in the orbital plane.

    @param times: times of observations (days)
    @type times: array
    @param parameters: list of parameters (P,e,a,T0)
    @type parameters: list
    @param component: component to calculate the orbit of. If it's the secondary,
    the angles will be shifted by 180 degrees
    @type component: string, one of 'primary' or 'secondary'
    @param coordinate_frame: type of coordinates
    @type coordinate_frame: str, ('polar' or 'cartesian')
    @return: coord1,coord2
    @rtype: 2xarray
    """
    #-- calculate the orbit in the plane
    r, theta = orbit_in_plane(times,
                              parameters,
                              component=component,
                              coordinate_frame='polar')
    P, e, a, T0 = parameters
    P = conversions.convert('d', 's', P)
    a = conversions.convert('au', 'm', a)

    #-- compute rdot and thetadot
    l = r * (1 + e * np.cos(theta))
    L = 2 * np.pi * a**2 / P * np.sqrt(1 - e**2)
    rdot = L / l * e * np.sin(theta)
    thetadot = L / r**2

    #-- convert to the right coordinate frame
    if coordinate_frame == 'polar':
        return rdot, thetadot
    elif coordinate_frame == 'cartesian':
        vx, vy, vz = vectors.spher2cart((r, theta, np.pi / 2.),
                                        (rdot, r * thetadot, 0))
        return vx, vy
Beispiel #10
0
def doppler_shift(wave, vrad, vrad_units='km/s', flux=None):
    """
    Shift a spectrum with towards the red or blue side with some radial velocity.

    You can give units with the extra keywords C{vrad_units} (units of
    wavelengths are not important). The shifted wavelengths will be in the same
    units as the input wave array.

    If units are not supplied, the radial velocity is assumed to be in km/s.

    If you want to apply a barycentric (or orbital) correction, you'd probabl
    want to reverse the sign of the radial velocity!

    When the keyword C{flux} is set, the spectrum will be interpolated onto
    the original wavelength grid (so the original wavelength grid will not
    change). When the keyword C{flux} is not set, the wavelength array will be
    changed (but the fluxes not, obviously).

    When C{flux} is set, fluxes will be returned.

    When C{flux} is not set, wavelengths will be returned.

    Example usage: shift a spectrum to the blue ('left') with 20 km/s.

    >>> wave = np.linspace(3000,8000,1000)
    >>> wave_shift1 = doppler_shift(wave,20.)
    >>> wave_shift2 = doppler_shift(wave,20000.,vrad_units='m/s')
    >>> print(wave_shift1[0],wave_shift1[-1])
    (3000.200138457119, 8000.5337025523177)
    >>> print(wave_shift2[0],wave_shift2[-1])
    (3000.200138457119, 8000.5337025523177)

    @param wave: wavelength array
    @type wave: ndarray
    @param vrad: radial velocity (negative shifts towards blue, positive towards red)
    @type vrad: float (units: km/s) or tuple (float,'units')
    @param vrad_units: units of radial velocity (default: km/s)
    @type vrad_units: str (interpretable for C{units.conversions.convert})
    @return: shifted wavelength array/shifted flux
    @rtype: ndarray
    """
    cc = constants.cc
    cc = conversions.convert('m/s', vrad_units, cc)
    wave_out = wave * (1 + vrad / cc)
    if flux is not None:
        flux = np.interp(wave, wave_out, flux)
        return flux
    else:
        return wave_out
Beispiel #11
0
    def set_date(self, startdate='today', days=20, dt=10.):
        """
        Set the start date for ephemeris calculations.

        'startdate' can take the following formats:
        Format 1: Calendar date: '2010/08/16 12:00:00.0' ,
        Format 2: Julian Date:  2455832.25 ,
        Format 3: None: no change, or 'today': the current date (=default)

        Careful: all times are UTC, they do not take care of time zones.
        default values are also set in UTC (gmtime)

        @keyword startdate: the exact time of the first visibility calculation
        @type startdate: string or float
        @keyword days: total number of days on which to calculate visibilities (if visibility keyword 'multiple' = False)
        @type days: float
        @keyword dt: sampling time in minutes
        @type dt: float

        """
        if startdate is None:
            startdate = self.thesite.date
        elif startdate is 'today':
            startdate = time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime())
        elif not isinstance(startdate, str):
            YYYY, MM, DD = conversions.convert('JD', 'CD', startdate)
            DD, frac = np.floor(DD), DD - np.floor(DD)
            hh = frac * 24.
            hh, frac = np.floor(hh), hh - np.floor(hh)
            mm = frac * 60.
            mm, frac = np.floor(mm), mm - np.floor(mm)
            ss = frac * 60.
            ss, frac = np.floor(ss), ss - np.floor(ss)
            startdate = '%4d/%02d/%02d %02d:%02d:%.1f' % (YYYY, MM, DD, hh, mm,
                                                          ss)
        if days is None:
            days = self.days
        if dt is None:
            dt = self.dt
        self.thesite.date = ephem.Date(startdate)
        self.dt = dt
        self.days = days

        if self.dt is None and self.days is None:
            logger.info('Set start date to %s' % (startdate))
        else:
            logger.info(
                'Set start date to %s, covering %d days per %d minutes' %
                (startdate, days, dt))
def doppler_shift(wave,vrad,vrad_units='km/s',flux=None):
    """
    Shift a spectrum with towards the red or blue side with some radial velocity.
    
    You can give units with the extra keywords C{vrad_units} (units of
    wavelengths are not important). The shifted wavelengths will be in the same
    units as the input wave array.
    
    If units are not supplied, the radial velocity is assumed to be in km/s.
    
    If you want to apply a barycentric (or orbital) correction, you'd probabl
    want to reverse the sign of the radial velocity!
    
    When the keyword C{flux} is set, the spectrum will be interpolated onto
    the original wavelength grid (so the original wavelength grid will not
    change). When the keyword C{flux} is not set, the wavelength array will be
    changed (but the fluxes not, obviously).
    
    When C{flux} is set, fluxes will be returned.
    
    When C{flux} is not set, wavelengths will be returned.
    
    Example usage: shift a spectrum to the blue ('left') with 20 km/s.
    
    >>> wave = np.linspace(3000,8000,1000)
    >>> wave_shift1 = doppler_shift(wave,20.)
    >>> wave_shift2 = doppler_shift(wave,20000.,vrad_units='m/s')
    >>> print(wave_shift1[0],wave_shift1[-1])
    (3000.200138457119, 8000.5337025523177)
    >>> print(wave_shift2[0],wave_shift2[-1])
    (3000.200138457119, 8000.5337025523177)
    
    @param wave: wavelength array
    @type wave: ndarray
    @param vrad: radial velocity (negative shifts towards blue, positive towards red)
    @type vrad: float (units: km/s) or tuple (float,'units')
    @param vrad_units: units of radial velocity (default: km/s)
    @type vrad_units: str (interpretable for C{units.conversions.convert})
    @return: shifted wavelength array/shifted flux
    @rtype: ndarray
    """ 
    cc = constants.cc
    cc = conversions.convert('m/s',vrad_units,cc)
    wave_out = wave * (1+vrad/cc)
    if flux is not None:
        flux = np.interp(wave,wave_out,flux)
        return flux
    else:
        return wave_out
    def set_date(self,startdate='today',days=20,dt=10.):
        """
        Set the start date for ephemeris calculations.

        'startdate' can take the following formats:
        Format 1: Calendar date: '2010/08/16 12:00:00.0' ,
        Format 2: Julian Date:  2455832.25 ,
        Format 3: None: no change, or 'today': the current date (=default)

        Careful: all times are UTC, they do not take care of time zones.
        default values are also set in UTC (gmtime)

        @keyword startdate: the exact time of the first visibility calculation
        @type startdate: string or float
        @keyword days: total number of days on which to calculate visibilities (if visibility keyword 'multiple' = False)
        @type days: float
        @keyword dt: sampling time in minutes
        @type dt: float

        """
        if startdate is None:
            startdate = self.thesite.date
        elif startdate is 'today':
            startdate = time.strftime('%Y/%m/%d %H:%M:%S',time.gmtime())
        elif not isinstance(startdate,str):
            YYYY,MM,DD = conversions.convert('JD','CD',startdate)
            DD,frac = np.floor(DD),DD-np.floor(DD)
            hh = frac*24.
            hh,frac = np.floor(hh),hh-np.floor(hh)
            mm = frac*60.
            mm,frac = np.floor(mm),mm - np.floor(mm)
            ss = frac*60.
            ss,frac = np.floor(ss),ss - np.floor(ss)
            startdate = '%4d/%02d/%02d %02d:%02d:%.1f'%(YYYY,MM,DD,hh,mm,ss)
        if days is None:
            days = self.days
        if dt is None:
            dt = self.dt
        self.thesite.date = ephem.Date(startdate)
        self.dt = dt
        self.days = days

        if self.dt is None and self.days is None:
            logger.info('Set start date to %s'%(startdate))
        else:
            logger.info('Set start date to %s, covering %d days per %d minutes'%(startdate,days,dt))
def _timestamp2jd(timestamp):
    """
    Convert the time stamp from a HERMES FITS 'date-avg' to Julian Date.

    @param timestamp: string from 'date-avg'
    @type timestamp: string
    @return: julian date
    @rtype: float
    """
    date, hour = timestamp.split("T")
    year, month, day = date.split("-")
    hour, minute, second = hour.split(":")
    year   = float(year)
    month  = float(month)
    day    = float(day)
    hour   = float(hour)
    minute = float(minute)
    second = float(second)
    return conversions.convert("CD","JD",(year, month, day, hour, minute, second))
Beispiel #15
0
def _timestamp2jd(timestamp):
    """
    Convert the time stamp from a HERMES FITS 'date-avg' to Julian Date.

    @param timestamp: string from 'date-avg'
    @type timestamp: string
    @return: julian date
    @rtype: float
    """
    date, hour = timestamp.split("T")
    year, month, day = date.split("-")
    hour, minute, second = hour.split(":")
    year   = float(year)
    month  = float(month)
    day    = float(day)
    hour   = float(hour)
    minute = float(minute)
    second = float(second)
    return conversions.convert("CD","JD",(year, month, day, hour, minute, second))
Beispiel #16
0
def critical_velocity(M, R_pole, units='km/s', definition=1):
    """
    Compute the critical velocity (km/s)
    
    Definition 1 from Cranmer and Owocki, 1995:
    
    v_c = 2 pi R_eq(omega_c) * omega_c
    
    Definition 2 from Townsend 2004:
    
    v_c = sqrt ( 2GM/3Rp )
    
    which both amount to the same value:
    
    >>> critical_velocity(1.,1.,definition=1)
    356.71131858379499
    >>> critical_velocity(1.,1.,definition=2)
    356.71131858379488
    
    @param M: mass (solar masses)
    @type M: float
    @param R_pole: polar radius (solar radii)
    @type R_pole: float
    @param units: if you wish other units than Hz, you can give them here
    @type units: string understandable by L{units.conversions.convert}
    @return: critical velocity in km/s
    @rtype: float
    """
    if definition == 1:
        omega_crit = critical_angular_velocity(M, R_pole)
        P = 2 * pi / omega_crit
        R_eq = get_fastrot_roche_radius(pi / 2., R_pole, 1.) * constants.Rsol
        veq = 2 * pi * R_eq / P
    elif definition == 2:
        veq = np.sqrt(2 * constants.GG * M * constants.Msol /
                      (3 * R_pole * constants.Rsol))
    veq = conversions.convert('m/s', units, veq)

    return veq
def orbit_on_sky(times,parameters,distance=None,component='primary'):
    """
    Construct an orbit projected on the sky.
    
    Parameters contains:
        1. period (days)
        2. eccentricity
        3. semi-major axis (au)
        4. time of periastron passage T0 (not x0!) (HJD)
        5. omega: the longitude of periastron gives the orientation of the
        orbit within in its own plane (radians)
        6. Omega: PA of ascending node (radians)
        7. i: inclination (radians), i=pi/2 is edge on
    
    You can give an extra parameter 'distance' as a tuple (value,'unit'). This
    will be used to convert the distances to angular scale (arcsec).
    
    Else, this function returns the distances in AU.
    
    See Hilditch p41 for a sketch of the coordinates. The difference with this
    is that all angles are inverted. In this approach, North is in the positive
    X direction, East is in the negative Y direction.
    """
    #-- divide parameters in 'in-plane' parameters and euler angles
    pars_in_plane,euler_angles = parameters[:4],parameters[4:]
    #-- construct the orbit in the orbital plane
    r,theta = orbit_in_plane(times,pars_in_plane,component=component)
    #-- and project in onto the sky according to the euler angles
    x,y,z = project_orbit(r,theta,euler_angles)
    
    #-- if necessary, convert the true distance to angular scale
    if distance is not None:
        d = conversions.convert(distance[1],'m',distance[0])
        x = 2*np.arctan(x/(2*d))/np.pi*180*3600
        y = 2*np.arctan(y/(2*d))/np.pi*180*3600
        z = 2*np.arctan(z/(2*d))/np.pi*180*3600
        return x,y,z
    else:
        return x/au,y/au,z/au
Beispiel #18
0
def orbit_on_sky(times, parameters, distance=None, component='primary'):
    """
    Construct an orbit projected on the sky.

    Parameters contains:
        1. period (days)
        2. eccentricity
        3. semi-major axis (au)
        4. time of periastron passage T0 (not x0!) (HJD)
        5. omega: the longitude of periastron gives the orientation of the
        orbit within in its own plane (radians)
        6. Omega: PA of ascending node (radians)
        7. i: inclination (radians), i=pi/2 is edge on

    You can give an extra parameter 'distance' as a tuple (value,'unit'). This
    will be used to convert the distances to angular scale (arcsec).

    Else, this function returns the distances in AU.

    See Hilditch p41 for a sketch of the coordinates. The difference with this
    is that all angles are inverted. In this approach, North is in the positive
    X direction, East is in the negative Y direction.
    """
    #-- divide parameters in 'in-plane' parameters and euler angles
    pars_in_plane, euler_angles = parameters[:4], parameters[4:]
    #-- construct the orbit in the orbital plane
    r, theta = orbit_in_plane(times, pars_in_plane, component=component)
    #-- and project in onto the sky according to the euler angles
    x, y, z = project_orbit(r, theta, euler_angles)

    #-- if necessary, convert the true distance to angular scale
    if distance is not None:
        d = conversions.convert(distance[1], 'm', distance[0])
        x = 2 * np.arctan(x / (2 * d)) / np.pi * 180 * 3600
        y = 2 * np.arctan(y / (2 * d)) / np.pi * 180 * 3600
        z = 2 * np.arctan(z / (2 * d)) / np.pi * 180 * 3600
        return x, y, z
    else:
        return x / au, y / au, z / au
def critical_velocity(M,R_pole,units='km/s',definition=1):
    """
    Compute the critical velocity (km/s)
    
    Definition 1 from Cranmer and Owocki, 1995:
    
    v_c = 2 pi R_eq(omega_c) * omega_c
    
    Definition 2 from Townsend 2004:
    
    v_c = sqrt ( 2GM/3Rp )
    
    which both amount to the same value:
    
    >>> critical_velocity(1.,1.,definition=1)
    356.71131858379499
    >>> critical_velocity(1.,1.,definition=2)
    356.71131858379488
    
    @param M: mass (solar masses)
    @type M: float
    @param R_pole: polar radius (solar radii)
    @type R_pole: float
    @param units: if you wish other units than Hz, you can give them here
    @type units: string understandable by L{units.conversions.convert}
    @return: critical velocity in km/s
    @rtype: float
    """
    if definition==1:
        omega_crit = critical_angular_velocity(M,R_pole)
        P = 2*pi/omega_crit
        R_eq = get_fastrot_roche_radius(pi/2.,R_pole,1.)*constants.Rsol
        veq = 2*pi*R_eq/P
    elif definition==2:
        veq = np.sqrt( 2*constants.GG * M*constants.Msol / (3*R_pole*constants.Rsol))
    veq = conversions.convert('m/s',units,veq)
        
    return veq
Beispiel #20
0
    def dobb(x, T, **kwargs):
        wave_units = kwargs.get('wave_units', 'AA')
        flux_units = kwargs.get('flux_units', 'erg/s/cm2/AA')
        #-- prepare input
        #-- what kind of units did we receive?
        curr_conv = constants._current_convention
        # X: wavelength/frequency
        x_unit_type = conversions.get_type(wave_units)
        x = conversions.convert(wave_units, curr_conv, x)
        # T: temperature
        if isinstance(T, tuple):
            T = conversions.convert(T[1], 'K', T[0])
        # Y: flux
        y_unit_type = conversions.change_convention('SI', flux_units)
        #-- if you give Jy vs micron, we need to first convert wavelength to frequency
        if y_unit_type == 'kg1 rad-1 s-2' and x_unit_type == 'length':
            x = conversions.convert(
                conversions._conventions[curr_conv]['length'], 'rad/s', x)
            x_unit_type = 'frequency'
        elif y_unit_type == 'kg1 m-1 s-3' and x_unit_type == 'frequency':
            x = conversions.convert(
                'rad/s', conversions._conventions[curr_conv]['length'], x)
            x_unit_type = 'length'
        #-- correct for rad
        if x_unit_type == 'frequency':
            x /= (2 * np.pi)
        print y_unit_type
        #-- run function
        I = fctn((x, x_unit_type), T)

        #-- prepare output
        disc_integrated = kwargs.get('disc_integrated', True)
        ang_diam = kwargs.get('ang_diam', None)
        if disc_integrated:
            I *= np.sqrt(2 * np.pi)
            if ang_diam is not None:
                scale = conversions.convert(ang_diam[1], 'sr',
                                            ang_diam[0] / 2.)
                I *= scale
        I = conversions.convert(curr_conv, flux_units, I)
        return I
 def dobb(x,T,**kwargs):
     wave_units = kwargs.get('wave_units','AA')
     flux_units = kwargs.get('flux_units','erg/s/cm2/AA')
     #-- prepare input
     #-- what kind of units did we receive?
     curr_conv = constants._current_convention
     # X: wavelength/frequency
     x_unit_type = conversions.get_type(wave_units)
     x = conversions.convert(wave_units,curr_conv,x)
     # T: temperature
     if isinstance(T,tuple):
         T = conversions.convert(T[1],'K',T[0])
     # Y: flux
     y_unit_type = conversions.change_convention('SI',flux_units)
     #-- if you give Jy vs micron, we need to first convert wavelength to frequency
     if y_unit_type=='kg1 rad-1 s-2' and x_unit_type=='length':
         x = conversions.convert(conversions._conventions[curr_conv]['length'],'rad/s',x)
         x_unit_type = 'frequency'
     elif y_unit_type=='kg1 m-1 s-3' and x_unit_type=='frequency':
         x = conversions.convert('rad/s',conversions._conventions[curr_conv]['length'],x)
         x_unit_type = 'length'
     #-- correct for rad
     if x_unit_type=='frequency':
         x /= (2*np.pi)
     print y_unit_type
     #-- run function
     I = fctn((x,x_unit_type),T)        
     
     #-- prepare output
     disc_integrated = kwargs.get('disc_integrated',True)
     ang_diam = kwargs.get('ang_diam',None)
     if disc_integrated:
         I *= np.sqrt(2*np.pi)
         if ang_diam is not None:
             scale = conversions.convert(ang_diam[1],'sr',ang_diam[0]/2.)
             I *= scale
     I = conversions.convert(curr_conv,flux_units,I)
     return I
Beispiel #22
0
def read_amdl(filename):
    """
    Read unformatted binary AMDL files from ADIPLS.
    
    @param filename: name of the file
    @type filename: str
    @return: global parameters, local parameters
    @rtype: dict, rec array
    """
    with open(filename, 'rb') as ff:
        #-- read in all the bytes at once
        content = ff.read()
    #-- have a look at the header of the file
    header_fmt = '<iii'
    line_fmt = '<' + 8 * 'd'
    head = struct.calcsize(header_fmt)
    line = struct.calcsize(line_fmt)
    header = content[:head]
    content = content[head:]
    print struct.unpack(header_fmt, header)
    model_name, icase, nn = struct.unpack(header_fmt, header)
    print filename, 'model_name,nn', model_name, nn

    #-- read in the first line of the data. This contains
    #   global information on the model, and also the number
    #   of columns in the datafile.
    record = content[:line]
    content = content[line:]
    M, R, P_c, rho_c, D5, D6, mu, D8 = struct.unpack(line_fmt, record)

    #-- print out some information
    M_ = conversions.convert('g', 'Msol', M)
    R_ = conversions.convert('cm', 'Rsol', R)
    print "Model information: (%d meshpoints)" % (nn)
    print "  M=%.3gMsol, R=%.3gRsol" % (M_, R_)
    print "  P_c=%.3g g/cm/s2, rho_c=%.3g g/cm3" % (P_c, rho_c)
    print '  Polytropic index: %.2g' % (mu)
    print '  %.3g %3g' % (D5, D6)

    #-- derive the number of variables in this model file
    if D8 >= 100: nvars = 9
    elif D8 >= 10: nvars = 7
    else: nvars = 6

    #-- then read in the rest, line by line
    line_fmt = '<' + nvars * 'd'
    line = struct.calcsize(line_fmt)
    starl = []
    for i in range(nn):
        record = content[:line]
        content = content[line:]
        data = struct.unpack(line_fmt, record)
        starl.append(data)

    #-- end of the file
    line_fmt = '<i'
    record = content[:line]
    content = content[line:]
    data = struct.unpack(line_fmt, record)
    print 'endoffile', data

    #-- wrap up the local and global information on the star
    starl = np.rec.fromarrays(np.array(starl).T,
                              names=['x', 'q/x3', 'Vg', 'G1', 'A', 'U'])
    starg = {
        'M': M,
        'R': R,
        'P_c': P_c,
        'rho_c': rho_c,
        'D5': D5,
        'D6': D6,
        'mu': mu
    }
    if len(content):
        raise ValueError, 'uninterpreted data in file', fileanem

    return starg, starl
Beispiel #23
0
def radial_velocity(parameters, times=None, theta=None, itermax=8):
    """
    Evaluate Radial velocities due to kepler orbit.

    These parameters define the Keplerian orbit if you give times points (C{times}, days):
        1. Period of the system (days)
        2. time of periastron passage T0 (not x0!) (HJD)
        3. eccentricity
        4. omega, the longitude of periastron gives the orientation
           of the orbit within its own plane (radians)
        5. the semiamplitude of the velocity curve (km/s)
        6. systemic velocity RV0 (RV of centre of mass of system) (km/s)

    These parameters define the Keplerian orbit if you give angles (C{theta}, radians):
        1. Period of the system (days)
        2. eccentricity
        3. a, the semi-major axis of the absolute orbit of the component (au)
        4. omega, the longitude of periastron gives the orientation of the
        orbit within in its own plane (radians)
        5. inclination of the orbit (radians)
        6. systemic velocity RV0 (RV of centre of mass of system) (km/s)

    The periastron passage T0 can be derived via x0 by calculating

    T0 = x0/(2pi*Freq) + times[0]

    See e.g. p 41,42 of Hilditch, 'An Introduction To Close Binary Stars'

    @parameter parameters: parameters of Keplerian orbit (dependent on input)
    @type parameters: iterable
    @parameter times: observation times (days)
    @type times: numpy array
    @parameter theta: orbital angles (radians)
    @type theta: numpy array
    @param itermax: number of iterations to find true anomaly
    @type itermax: integer
    @return: fitted radial velocities (km/s)
    @rtype: ndarray
    """

    #-- in the case time points are given:
    if times is not None:
        #-- expand parameters and calculate x0
        P, T0, e, omega, K, RV0 = parameters
        freq = 1. / P
        x0 = T0 * 2 * np.pi * freq
        #-- calculate true anomaly
        E, true_an = true_anomaly(times * 2 * np.pi * freq - x0,
                                  e,
                                  itermax=itermax)
        #-- evaluate Keplerian radial velocity orbit
        RVfit = RV0 + K * (e * np.cos(omega) + np.cos(true_an + omega))

    elif theta is not None:
        P, e, a, omega, i, RV0 = parameters
        P = conversions.convert('d', 's', P)
        a = conversions.convert('au', 'm', a)
        K = 2 * np.pi * a * np.sin(i) / (P * np.sqrt(1. - e**2))
        RVfit = RV0 + K * (e * np.cos(omega) + np.cos(theta + omega)) / 1000.

    return RVfit
Beispiel #24
0
def helcorr(ra2000,
            dec2000,
            jd,
            obs_long=None,
            obs_lat=None,
            obs_alt=None,
            debug=False):
    """
  calculates heliocentric Julian date, baricentric and heliocentric radial
  velocity corrections from:

  INPUT:
  @param ra2000: Right ascension of object for epoch 2000.0 (hours)
  @type ra2000: float
  @param dec2000: declination of object for epoch 2000.0 (degrees)
  @type dec2000: float
  @param jd: julian date for the middle of exposure
  @type jd: float
  @keyword obs_long: longitude of observatory (degrees, western direction is positive)
  @type obs_long: float
  @keyword obs_lat: latitude of observatory (degrees)
  @type obs_lat: float
  @keyword obs_alt: altitude of observatory (meters)
  @type obs_alt: float
  
  Algorithms used are taken from the IRAF task noao.astutils.rvcorrect
  and some procedures of the IDL Astrolib are used as well.
  Accuracy is about 0.5 seconds in time and about 1 m/s in velocity.
  
  History:
  written by Peter Mittermayer, Nov 8,2003
  2005-January-13   Kudryavtsev   Made more accurate calculation of the sideral time.
                                  Conformity with MIDAS compute/barycorr is checked.
  2005-June-20      Kochukhov Included precession of RA2000 and DEC2000 to current epoch
  """
    _radeg = 180.0 / np.pi

    # If observatory not given, set La Palma
    if obs_long is None:
        obs_long = 17.878333
    if obs_lat is None:
        obs_lat = 28.762222
    if obs_alt is None:
        obs_alt = 2333.

    #covert JD to Gregorian calendar date
    xjd = jd * 1.0
    jd = jd - 2400000.0
    year, month, dayraw = convert("JD", "CD", xjd)
    day = dayraw - dayraw % 24.
    ut = 24. * (dayraw % 24)

    #current epoch
    epoch = year + month / 12. + day / 365.

    #precess ra2000 and dec2000 to current epoch
    ra, dec = precess(ra2000 * 15., dec2000, 2000.0, epoch)

    #calculate heliocentric julian date
    hjd = np.array(helio_jd(jd, ra, dec)).astype(float)

    #DIURNAL VELOCITY (see IRAF task noao.astutil.rvcorrect)
    dlat = -(11. * 60. + 32.743) * np.sin(
        2 * obs_lat / _radeg) + 1.1633 * np.sin(
            4 * obs_lat / _radeg) - 0.0026 * np.sin(6 * obs_lat / _radeg)
    lat = obs_lat + dlat / 3600

    #calculate distance of observer from earth center
    r = 6378160.0 * (0.998327073 + 0.001676438 * np.cos(2 * lat / _radeg) -
                     0.00000351 * np.cos(4 * lat / _radeg) +
                     0.000000008 * np.cos(6 * lat / _radeg)) + obs_alt

    #calculate rotational velocity (perpendicular to the radius vector) in km/s
    #23.934469591229 is the siderial day in hours for 1986
    v = 2. * np.pi * (r / 1000.) / (23.934469591229 * 3600.)

    #calculating local mean siderial time (see astronomical almanach)
    tu = (jd - 51545.0) / 36525
    gmst = 6.697374558 + ut + (236.555367908 * (jd - 51545.0) +
                               0.093104 * tu**2 - 6.2e-6 * tu**3) / 3600
    lmst = (gmst - obs_long / 15) % 24

    #projection of rotational velocity along the line of sight
    vdiurnal = v * np.cos(lat / _radeg) * np.cos(dec / _radeg) * np.sin(
        (ra - lmst * 15) / _radeg)

    #BARICENTRIC and HELIOCENTRIC VELOCITIES
    vh, vb = baryvel(xjd, 0)

    #project to line of sight
    vbar = vb[0] * np.cos(dec / _radeg) * np.cos(ra / _radeg) + vb[1] * np.cos(
        dec / _radeg) * np.sin(ra / _radeg) + vb[2] * np.sin(dec / _radeg)
    vhel = vh[0] * np.cos(dec / _radeg) * np.cos(ra / _radeg) + vh[1] * np.cos(
        dec / _radeg) * np.sin(ra / _radeg) + vh[2] * np.sin(dec / _radeg)
    corr = (vdiurnal + vbar)  #using baricentric velocity for correction

    return (corr, hjd)
Beispiel #25
0
def write_gyre(starg, starl, filename):
    """
    Write a file to the GYRE format.
    """
    gyre_model = {}

    gyre_model['L_star'] = float(
        conversions.convert('erg/s', "SI", starg['photosphere_L']))
    gyre_model['R_star'] = float(
        conversions.convert('cm', "SI", starg['photosphere_r']))
    gyre_model['X_star'] = -1.00
    gyre_model['Z_star'] = float(starg['initial_z'])
    gyre_model['M_star'] = float(
        conversions.convert('g', 'SI', starg['star_mass']))
    gyre_model['t_star'] = float(starg['star_age'])
    gyre_model['n_shells'] = len(starl)
    gyre_model['B3_VERSION'] = 1.0
    gyre_model['B3_SUBCLASS'] = ''
    gyre_model['B3_CLASS'] = 'STAR'
    gyre_model['B3_DATE'] = ''
    #for key in gyre_model:
    #print key,gyre_model[key]

    #-- local parameters
    #inner = starl['mass']<starg['star_mass']
    #w = -starl['mass'][inner]/(starl['mass'][inner]-starg['star_mass'])
    inner = starl['mass'] < starl['mass'].max()
    w = -starl['mass'][inner] / (starl['mass'][inner] - starl['mass'].max())
    gyre_model['r'] = conversions.convert('cm', 'SI', starl['radius'])
    gyre_model['w'] = np.hstack([w, 1e16 * np.ones(sum(-inner))])
    gyre_model['L_r'] = conversions.convert('erg/s', 'SI', starl['luminosity'])
    gyre_model['L_r'][0] = 0.
    gyre_model['T'] = starl['temperature']
    gyre_model['p'] = conversions.convert('ba', 'Pa', starl['pressure'])
    gyre_model['c_V'] = conversions.convert('erg/s/g/K', 'SI', starl['cv'])
    gyre_model['c_p'] = conversions.convert('erg/s/g/K', 'SI', starl['cp'])
    gyre_model['X'] = starl['h1']
    gyre_model['rho'] = conversions.convert('g/cm3', 'SI', starl['Rho'])
    gyre_model['chi_rho'] = starl['chiRho']
    gyre_model['chi_T'] = starl['chiT']
    gyre_model['N2'] = starl['brunt_N2']
    gyre_model['nabla'] = starl['grad_temperature']
    gyre_model['kappa'] = conversions.convert('cm2/g', 'SI', starl['opacity'])
    gyre_model['kappa_rho'] = starl['dkap_dlnrho'] / starl['opacity']
    gyre_model['kappa_T'] = starl['dkap_dlnT'] / starl['opacity']
    gyre_model['epsilon'] = np.zeros(len(starl))
    gyre_model['epsilon_rho'] = np.zeros(len(starl))
    gyre_model['epsilon_T'] = np.zeros(len(starl))

    #-- global parameters
    gyre_model['L_star'] = float(gyre_model['L_r'].max())
    gyre_model['R_star'] = float(gyre_model['r'].max())
    gyre_model['M_star'] = float(starl['mass'].max() / 1000.)
    #gyre_model['L_star'] = float(gyre_model['L_r'].max())#float(conversions.convert('erg/s',"SI",starg['photosphere_L']))
    #gyre_model['R_star'] = float(gyre_model['r'].max())#float(conversions.convert('cm',"SI",starg['photosphere_r']))
    #gyre_model['X_star'] = -1.00
    #gyre_model['Z_star'] = float(starg['initial_z'])
    #gyre_model['M_star'] = float(starl['mass'].max())#float(conversions.convert('g','SI',starg['star_mass']))
    #gyre_model['t_star'] = float(starg['star_age'])
    #gyre_model['n_shells'] = len(starl)
    #gyre_model['B3_VERSION'] = 1.0
    #gyre_model['B3_SUBCLASS'] = ''
    #gyre_model['B3_CLASS'] = 'STAR'
    #gyre_model['B3_DATE'] = ''

    #keys = ['w','L_r','T','p','c_V','c_p','X','rho','chi_rho','chi_T','N2',\
    #'nabla','kappa','kappa_rho','kappa_T','epsilon','epsilon_rho','epsilon_T']
    ##for key in keys:
    #gyre_model[key] = np.array(gyre_model[key],np.float32)
    #print key,gyre_model[key].min(),gyre_model[key].max()

    hdf5.write_dict(gyre_model,
                    filename,
                    update=False,
                    attr_types=[float, int, str, unicode])
Beispiel #26
0
def get_law(name, norm='E(B-V)', wave_units='AA', photbands=None, **kwargs):
    """
    Retrieve an interstellar reddening law.
    
    Parameter C{name} must be the function name of one of the laws defined in
    this module.
    
    By default, the law will be interpolated on a grid from 100 angstrom to
    10 micron in steps of 10 angstrom. This can be adjusted with the parameter
    C{wave} (array), which B{must} be in angstrom. You can change the units
    ouf the returned wavelength array via C{wave_units}.
    
    By default, the curve is normalised with respect to E(B-V) (you get
    A(l)/E(B-V)). You can set the C{norm} keyword to Av if you want A(l)/Av.
    Remember that
    
    A(V) = Rv * E(B-V)
    
    The parameter C{Rv} is by default 3.1, other reasonable values lie between
    2.0 and 5.1
    
    Extra accepted keywords depend on the type of reddening law used.
    
    Example usage:
    
    >>> wave = np.r_[1e3:1e5:10]
    >>> wave,mag = get_law('cardelli1989',wave=wave,Rv=3.1)
    
    @param name: name of the interstellar law
    @type name: str, one of the functions defined here
    @param norm: type of normalisation of the curve
    @type norm: str (one of E(B-V), Av)
    @param wave_units: wavelength units
    @type wave_units: str (interpretable for units.conversions.convert)
    @param photbands: list of photometric passbands
    @type photbands: list of strings
    @keyword wave: wavelength array to interpolate the law on
    @type wave: ndarray
    @return: wavelength, reddening magnitude
    @rtype: (ndarray,ndarray)
    """
    #-- get the inputs
    wave_ = kwargs.pop('wave', None)
    Rv = kwargs.setdefault('Rv', 3.1)

    #-- get the curve
    wave, mag = globals()[name.lower()](**kwargs)
    wave_orig, mag_orig = wave.copy(), mag.copy()

    #-- interpolate on user defined grid
    if wave_ is not None:
        if wave_units != 'AA':
            wave_ = conversions.convert(wave_units, 'AA', wave_)
        mag = np.interp(wave_, wave, mag, right=0)
        wave = wave_

    #-- pick right normalisation: convert to A(lambda)/Av if needed
    if norm.lower() == 'e(b-v)':
        mag *= Rv
    else:
        #-- we allow ak and av as shortcuts for normalisation in JOHNSON K and
        #   V bands
        if norm.lower() == 'ak':
            norm = 'JOHNSON.K'
        elif norm.lower() == 'av':
            norm = 'JOHNSON.V'
        norm_reddening = model.synthetic_flux(wave_orig, mag_orig, [norm])[0]
        logger.info('Normalisation via %s: Av/%s = %.6g' %
                    (norm, norm, 1. / norm_reddening))
        mag /= norm_reddening

    #-- maybe we want the curve in photometric filters
    if photbands is not None:
        mag = model.synthetic_flux(wave, mag, photbands)
        wave = filters.get_info(photbands)['eff_wave']

    #-- set the units of the wavelengths
    if wave_units != 'AA' and photbands is not None:
        wave = conversions.convert('AA', wave_units, wave)

    return wave, mag
Beispiel #27
0
def read_corot(fits_file,
               return_header=False,
               type_data='hel',
               remove_flagged=True):
    """
    Read CoRoT data from a CoRoT FITS file.

    Both SISMO and EXO data are recognised and extracted accordingly.

    type_data is one of:
        - type_data='raw'
        - type_data='hel': heliocentric unequidistant
        - type_data='helreg': heliocentric equidistant

    @param fits_file: CoRoT FITS file name
    @type fits_file: string
    @param return_header: return header information as dictionary
    @type return_header: bool
    @param type_data: type of data to return
    @type type_data: string (one of 'raw','hel' or 'helreg')
    @param remove_flagged: remove flagged datapoints
    @type remove_flagged: True
    @return: CoRoT data (times, flux, error, flags)
    @rtype: array, array, array, array(, header)
    """
    #-- read in the FITS file
    # headers: ['DATE', 'DATEJD', 'DATEHEL', 'STATUS', 'WHITEFLUX', 'WHITEFLUXDEV', 'BG', 'CORREC']
    fits_file_ = pf.open(fits_file)
    if fits_file_[0].header['hlfccdid'][0] == 'A':
        times,flux,error,flags = fits_file_[type_data].data.field(0),\
                                 fits_file_[type_data].data.field(1),\
                                 fits_file_[type_data].data.field(2),\
                                 fits_file_[type_data].data.field(3)
        # extract the header if asked
        if return_header:
            header = fits_file_[0].header
        fits_file_.close()

        logger.debug('Read CoRoT SISMO file %s' % (fits_file))
    elif fits_file_[0].header['hlfccdid'][0] == 'E':
        times = fits_file_['bintable'].data.field('datehel')
        if 'blueflux' in fits_file_['bintable'].columns.names:
            blueflux, e_blueflux = fits_file_['bintable'].data.field(
                'blueflux'), fits_file_['bintable'].data.field('bluefluxdev')
            greenflux, e_greenflux = fits_file_['bintable'].data.field(
                'greenflux'), fits_file_['bintable'].data.field('greenfluxdev')
            redflux, e_redflux = fits_file_['bintable'].data.field(
                'redflux'), fits_file_['bintable'].data.field('redfluxdev')
            #-- chromatic light curves
            if type_data == 'colors':
                flux = np.column_stack([blueflux, greenflux, redflux])
                error = np.column_stack([e_blueflux, e_greenflux,
                                         e_redflux]).min(axis=1)
            #-- white light curves
            else:
                flux = blueflux + greenflux + redflux
                error = np.sqrt(e_blueflux**2 + e_greenflux**2 + e_redflux**2)
        else:
            flux, error = fits_file_['bintable'].data.field(
                'whiteflux'), fits_file_['bintable'].data.field('whitefluxdev')
        flags = fits_file_['bintable'].data.field('status')

    # remove flagged datapoints if asked
    if remove_flagged:
        keep1 = (flags == 0)
        keep2 = (error != -1)
        logger.info(
            'Remove: flagged (%d) no valid error (%d) datapoints (%d)' %
            (len(keep1) - keep1.sum(), len(keep2) - keep2.sum(), len(keep1)))
        keep = keep1 & keep2
        times, flux, error, flags = times[keep], flux[keep], error[
            keep], flags[keep]

    # convert times to heliocentric JD
    times = conversions.convert('MJD', 'JD', times, jtype='corot')

    if return_header:
        return times, flux, error, flags, header
    else:
        return times, flux, error, flags
def combine(list_of_spectra,R=200.,lambda0=(950.,'AA'),lambdan=(3350.,'AA')):
    """
    Combine and weight-average spectra on a common wavelength grid.
    
    C{list_of_spectra} should be a list of lists/arrays. Each element in the
    main list should be (wavelength,flux,error).
    
    If you have FUSE fits files, use L{ivs.fits.read_fuse}.
    If you have IUE FITS files, use L{ivs.fits.read_iue}.
    
    After Peter Woitke.
    
    @param R: resolution
    @type R: float
    @param lambda0: start wavelength, unit
    @type lambda0: tuple (float,str)
    @param lambdan: end wavelength, unit
    @type lambdan: tuple (float,str)
    @return: binned spectrum (wavelengths,flux, error)
    @rtype: array, array, array
    """
    l0 = conversions.convert(lambda0[1],'AA',lambda0[0])
    ln = conversions.convert(lambdan[1],'AA',lambdan[0])
    #-- STEP 1: define wavelength bins
    Delta = np.log10(1.+1./R)
    x = np.arange(np.log10(l0),np.log10(ln)+Delta,Delta)
    x = 10**x
    lamc_j = 0.5*(np.roll(x,1)+x)

    #-- STEP 2: rebinning of data onto newly defined wavelength bins
    Ns = len(list_of_spectra)
    Nw = len(lamc_j)-1
    binned_fluxes = np.zeros((Ns,Nw))
    binned_errors = np.inf*np.ones((Ns,Nw))

    for snr,(wave,flux,err) in enumerate(list_of_spectra):
        wave0 = np.roll(wave,1)
        wave1 = np.roll(wave,-1)
        lam_i0_dc = 0.5*(wave0+wave)
        lam_i1_dc = 0.5*(wave1+wave)
        dlam_i = lam_i1_dc-lam_i0_dc
        
        for j in range(Nw):
            A = np.min(np.vstack([lamc_j[j+1]*np.ones(len(wave)),lam_i1_dc]),axis=0)
            B = np.max(np.vstack([lamc_j[j]*np.ones(len(wave)),lam_i0_dc]),axis=0)
            overlaps = scipy.stats.threshold(A-B,threshmin=0)
            norm = np.sum(overlaps)
            binned_fluxes[snr,j] = np.sum(flux*overlaps)/norm
            binned_errors[snr,j] = np.sqrt(np.sum((err*overlaps)**2))/norm
    
    #-- STEP 3: all available spectra sets are co-added, using the inverse
    #   square of the bin uncertainty as weight
    binned_fluxes[np.isnan(binned_fluxes)] = 0
    binned_errors[np.isnan(binned_errors)] = 1e300
    weights = 1./binned_errors**2
    
    totalflux = np.sum(weights*binned_fluxes,axis=0)/np.sum(weights,axis=0)
    totalerr = np.sqrt(np.sum((weights*binned_errors)**2,axis=0))/np.sum(weights,axis=0)
    totalspec = np.sum(binned_fluxes>0,axis=0)
    
    #-- that's it!
    return x[:-1],totalflux,totalerr,totalspec
def get_photometry(ID=None,extra_fields=['dist','ra','dec'],**kwargs):
    """
    Download all available photometry from a star to a record array.
    
    For extra kwargs, see L{_get_URI} and L{gator2phot}
    
    Example usage:
    
    >>> import pylab
    >>> import vizier
    >>> name = 'kr cam'
    >>> master = vizier.get_photometry(name,to_units='erg/s/cm2/AA',extra_fields=[])
    >>> master = get_photometry(name,to_units='erg/s/cm2/AA',extra_fields=[],master=master)
    >>> p = pylab.figure()
    >>> wise = np.array(['WISE' in photband and True or False for photband in master['photband']])
    >>> p = pylab.errorbar(master['cwave'],master['cmeas'],yerr=master['e_cmeas'],fmt='ko')
    >>> p = pylab.errorbar(master['cwave'][wise],master['cmeas'][wise],yerr=master['e_cmeas'][wise],fmt='ro',ms=8)
    >>> p = pylab.gca().set_xscale('log')
    >>> p = pylab.gca().set_yscale('log')
    >>> p = pylab.show()
    
    Other examples:
    >>> master = get_photometry(ra=71.239527,dec=-70.589427,to_units='erg/s/cm2/AA',extra_fields=[],radius=1.)
    >>> master = get_photometry(ID='J044458.39-703522.6',to_units='W/m2',extra_fields=[],radius=1.)
    """
    kwargs['ID'] = ID
    to_units = kwargs.pop('to_units','erg/s/cm2/AA')
    master_ = kwargs.get('master',None)
    master = None
    #-- retrieve all measurements
    for source in cat_info.sections():
        results,units,comms = search(source,**kwargs)
        if results is not None:
            master = gator2phot(source,results,units,master,extra_fields=extra_fields)
    
    #-- convert the measurement to a common unit.
    if to_units and master is not None:
        #-- prepare columns to extend to basic master
        dtypes = [('cwave','f8'),('cmeas','f8'),('e_cmeas','f8'),('cunit','a50')]
        cols = [[],[],[],[]]
        #-- forget about 'nan' errors for the moment
        no_errors = np.isnan(master['e_meas'])
        master['e_meas'][no_errors] = 0.
        #-- extend basic master
        zp = filters.get_info(master['photband'])
        for i in range(len(master)):
            try:
                value,e_value = conversions.convert(master['unit'][i],to_units,master['meas'][i],master['e_meas'][i],photband=master['photband'][i])
            except ValueError: # calibrations not available
                value,e_value = np.nan,np.nan
            except AssertionError: # the error or flux must be positive number
                value,e_value = np.nan,np.nan
            try:
                eff_wave = filters.eff_wave(master['photband'][i])
            except IOError:
                eff_wave = np.nan
            cols[0].append(eff_wave)
            cols[1].append(value)
            cols[2].append(e_value)
            cols[3].append(to_units)
        master = numpy_ext.recarr_addcols(master,cols,dtypes)
        #-- reset errors
        master['e_meas'][no_errors] = np.nan
        master['e_cmeas'][no_errors] = np.nan
    
    if master_ is not None and master is not None:
        master = numpy_ext.recarr_addrows(master_,master.tolist())
    elif master_ is not None:
        master = master_
    
    #-- and return the results
    return master
Beispiel #30
0
        if 'plx' in database and not ('2007' in database['plx']['r']):
            data,units,comms = vizier.search('I/311/hip2',ID=ID)
            if data is not None and len(data):
                if not 'plx' in database:
                    database['plx'] = {}
                database['plx']['v'] = data['Plx'][0]
                database['plx']['e'] = data['e_Plx'][0]
                database['plx']['r'] = 'I/311/hip2'
        #-- fix the spectral type
        data,units,comms = vizier.search('B/mk/mktypes',ID=ID)
        if data is not None and len(data):
            database['spType'] = data['SpType'][0]
        if 'jpos' in database:
            #-- add galactic coordinates (in degrees)
            ra,dec = database['jpos'].split()
            gal = conversions.convert('equatorial','galactic',(str(ra),str(dec)),epoch='2000')
            gal = float(gal[0])/np.pi*180,float(gal[1])/np.pi*180
            database['galpos'] = gal
        #-- fix the proper motions
        data,units,comms = vizier.search('I/317/sample',ID=ID)
        if data is not None and len(data):
            if not 'pm' in database:
                database['pm'] = {}
            database['pm']['pmRA'] = data['pmRA'][0]
            database['pm']['pmDE'] = data['pmDE'][0]
            database['pm']['epmRA'] = data['e_pmRA'][0]
            database['pm']['epmDE'] = data['e_pmDE'][0]
            database['pm']['r'] = 'I/317/sample'
    return database
    
if __name__=="__main__":
def get_photometry(ID=None,extra_fields=[],**kwargs):
    """
    Download all available photometry from a star to a record array.
    
    Extra fields will not be useful probably.
    
    For extra kwargs, see L{_get_URI} and L{gcpd2phot}
    """
    to_units = kwargs.pop('to_units','erg/s/cm2/AA')
    master_ = kwargs.get('master',None)
    master = None
    #-- retrieve all measurements
    for source in cat_info.sections():
        results,units,comms = search(source,ID=ID,**kwargs)
        if results is not None:
            master = gcpd2phot(source,results,units,master,extra_fields=extra_fields)
    
    #-- convert the measurement to a common unit.
    if to_units and master is not None:
        #-- prepare columns to extend to basic master
        dtypes = [('cwave','f8'),('cmeas','f8'),('e_cmeas','f8'),('cunit','a50')]
        cols = [[],[],[],[]]
        #-- forget about 'nan' errors for the moment
        no_errors = np.isnan(master['e_meas'])
        master['e_meas'][no_errors] = 0.
        #-- extend basic master
        try:
            zp = filters.get_info(master['photband'])
        except:
            print master['photband']
            raise
        for i in range(len(master)):
            to_units_ = to_units+''
            try:
                value,e_value = conversions.convert(master['unit'][i],to_units,master['meas'][i],master['e_meas'][i],photband=master['photband'][i])
            except ValueError: # calibrations not available
                # if it is a magnitude color, try converting it to a flux ratio
                if 'mag' in master['unit'][i]:
                    try:
                        value,e_value = conversions.convert('mag_color','flux_ratio',master['meas'][i],master['e_meas'][i],photband=master['photband'][i])
                        to_units_ = 'flux_ratio'
                    except ValueError:
                        value,e_value = np.nan,np.nan
                # else, we are powerless...
                else:
                    value,e_value = np.nan,np.nan
            try:
                eff_wave = filters.eff_wave(master['photband'][i])
            except IOError:
                eff_wave = np.nan
            cols[0].append(eff_wave)
            cols[1].append(value)
            cols[2].append(e_value)
            cols[3].append(to_units_)
        master = numpy_ext.recarr_addcols(master,cols,dtypes)
        #-- reset errors
        master['e_meas'][no_errors] = np.nan
        master['e_cmeas'][no_errors] = np.nan
    
    #-- if a master is given as a keyword, and data is found in this module,
    #   append the two
    if master_ is not None and master is not None:
        master = numpy_ext.recarr_addrows(master_,master.tolist())
    elif master is None:
        master = master_
    #-- and return the results
    return master
def write_gyre(starg,starl,filename):
    """
    Write a file to the GYRE format.
    """
    gyre_model = {}
    
    gyre_model['L_star'] = float(conversions.convert('erg/s',"SI",starg['photosphere_L']))
    gyre_model['R_star'] = float(conversions.convert('cm',"SI",starg['photosphere_r']))
    gyre_model['X_star'] = -1.00
    gyre_model['Z_star'] = float(starg['initial_z'])
    gyre_model['M_star'] = float(conversions.convert('g','SI',starg['star_mass']))
    gyre_model['t_star'] = float(starg['star_age'])
    gyre_model['n_shells'] = len(starl)
    gyre_model['B3_VERSION'] = 1.0
    gyre_model['B3_SUBCLASS'] = ''
    gyre_model['B3_CLASS'] = 'STAR'
    gyre_model['B3_DATE'] = ''
    #for key in gyre_model:
        #print key,gyre_model[key]
    
    #-- local parameters
    #inner = starl['mass']<starg['star_mass']
    #w = -starl['mass'][inner]/(starl['mass'][inner]-starg['star_mass'])
    inner = starl['mass']<starl['mass'].max()
    w = -starl['mass'][inner]/(starl['mass'][inner]-starl['mass'].max())
    gyre_model['r'] = conversions.convert('cm','SI',starl['radius'])
    gyre_model['w'] =  np.hstack([w,1e16*np.ones(sum(-inner))])
    gyre_model['L_r'] = conversions.convert('erg/s','SI',starl['luminosity'])
    gyre_model['L_r'][0] = 0.
    gyre_model['T'] = starl['temperature']
    gyre_model['p'] = conversions.convert('ba','Pa',starl['pressure'])
    gyre_model['c_V'] = conversions.convert('erg/s/g/K','SI',starl['cv'])
    gyre_model['c_p'] = conversions.convert('erg/s/g/K','SI',starl['cp'])
    gyre_model['X'] = starl['h1']
    gyre_model['rho'] = conversions.convert('g/cm3','SI',starl['Rho'])
    gyre_model['chi_rho'] = starl['chiRho']
    gyre_model['chi_T'] = starl['chiT']
    gyre_model['N2'] = starl['brunt_N2']
    gyre_model['nabla'] = starl['grad_temperature']
    gyre_model['kappa'] = conversions.convert('cm2/g','SI',starl['opacity'])
    gyre_model['kappa_rho'] = starl['dkap_dlnrho']/starl['opacity']
    gyre_model['kappa_T'] = starl['dkap_dlnT']/starl['opacity']
    gyre_model['epsilon'] = np.zeros(len(starl))
    gyre_model['epsilon_rho'] = np.zeros(len(starl))
    gyre_model['epsilon_T'] = np.zeros(len(starl))
    
    #-- global parameters
    gyre_model['L_star'] = float(gyre_model['L_r'].max())
    gyre_model['R_star'] = float(gyre_model['r'].max())
    gyre_model['M_star'] = float(starl['mass'].max()/1000.)
    #gyre_model['L_star'] = float(gyre_model['L_r'].max())#float(conversions.convert('erg/s',"SI",starg['photosphere_L']))
    #gyre_model['R_star'] = float(gyre_model['r'].max())#float(conversions.convert('cm',"SI",starg['photosphere_r']))
    #gyre_model['X_star'] = -1.00
    #gyre_model['Z_star'] = float(starg['initial_z'])
    #gyre_model['M_star'] = float(starl['mass'].max())#float(conversions.convert('g','SI',starg['star_mass']))
    #gyre_model['t_star'] = float(starg['star_age'])
    #gyre_model['n_shells'] = len(starl)
    #gyre_model['B3_VERSION'] = 1.0
    #gyre_model['B3_SUBCLASS'] = ''
    #gyre_model['B3_CLASS'] = 'STAR'
    #gyre_model['B3_DATE'] = ''
    
    #keys = ['w','L_r','T','p','c_V','c_p','X','rho','chi_rho','chi_T','N2',\
            #'nabla','kappa','kappa_rho','kappa_T','epsilon','epsilon_rho','epsilon_T']
    ##for key in keys:
        #gyre_model[key] = np.array(gyre_model[key],np.float32)
        #print key,gyre_model[key].min(),gyre_model[key].max()
    
    hdf5.write_dict(gyre_model,filename,update=False,attr_types=[float,int,str,unicode])
def search(ID, db='S', fix=False):
    """
    Query Simbad, NED and/or Vizier for information on an identifier.

    This retrieves basic information on a star, e.g. as shown in a typical
    Simbad page: coordinates, spectral type, fluxes, aliases, references...

    Database C{db} is one of 'S' (SIMBAD), 'N' (NED), 'V' (Vizier)
    or 'A' (all).

    This function returns a (sometimes nested) dictionary. Example output is
    given below, where nested dictionaries are shown with the separator '.'
    between the keys.

    If you set C{fix} to C{False}, following values will be updated:

        1. the spectral type will be replaced by the one from the Skiff (2010)
        catalog if possible.
        2. The parallax will be replaced with the value from the new Van Leeuwen
        reduction.
        3. The galactic coordinates will be added (converted from RA and DEC)
        4. The proper motions will be taken from the PPMXL catalog from Roeser
        2010.

    Example usage:

    >>> info = search('vega',db='S')
    >>> print (info['jpos'])
    18:36:56.33 +38:47:01.2
    >>> print (info['jdedeg'])
    38.78369194
    >>> print (info['alias'][1])
    * alf Lyr
    >>> print (info['plx']['v'])
    128.93
    >>> print (info['mag']['B']['v'])
    0.03

    This is an exhaustive list of example contents::
        Vel.e    = 0.9
        Vel.q    = A
        Vel.r    = 1979IAUS...30...57E
        Vel.v    = -13.9
        alias    = [u'V* alf Lyr', u'* alf Lyr', u'* 3 Lyr', u'ADS 11510 A', u'AG+38 1711', u'ASCC 507896', u'BD+38 3238', u'CCDM J18369+3847A', u'CEL 4636', u'CSI+38 3238 1', u'CSV 101745', u'1E 183515+3844.3', u'EUVE J1836+38.7', u'FK5 699', u'GC 25466', u'GCRV 11085', u'GEN# +1.00172167', u'GJ 721', u'HD 172167', u'HGAM 706', u'HIC 91262', u'HIP 91262', u'HR 7001', u'IDS 18336+3841 A', u'IRAS 18352+3844', u'IRC +40322', u'JP11 2999', u'LSPM J1836+3847', u'LTT 15486', u'2MASS J18365633+3847012', u'N30 4138', u'NAME VEGA', u'NLTT 46746', u'NSV 11128', u'8pc 128.93', u'PLX 4293.00', u'PLX 4293', u'PMC 90-93 496', u'PPM 81558', u'RAFGL 2208', u'ROT 2633', u'SAO 67174', u'SKY# 34103', u'TD1 22883', u'TYC 3105-2070-1', u'UBV 15842', u'UBV M 23118', u'USNO-B1.0 1287-00305764', u'USNO 882', u'uvby98 100172167 V', u'WDS J18369+3846A', u'Zkh 277', u'[HFE83] 1223']
        errDEmas = 5.4
        errRAmas = 5.16
        jdedeg   = 38.78369194
        jpos     = 18:36:56.33 +38:47:01.2
        jradeg   = 279.234735
        mag.B.q  = C
        mag.B.v  = 0.03
        mag.H.q  = C
        mag.H.r  = 2003yCat.2246....0C
        mag.H.v  = -0.03
        mag.I.q  = E
        mag.I.r  = 2003AJ....125..984M
        mag.I.v  = 0.2
        mag.J.q  = C
        mag.J.r  = 2003yCat.2246....0C
        mag.J.v  = -0.18
        mag.K.q  = C
        mag.K.r  = 2003yCat.2246....0C
        mag.K.v  = 0.13
        mag.R.q  = E
        mag.R.r  = 2003AJ....125..984M
        mag.R.v  = 0.1
        mag.V.q  = C
        mag.V.v  = 0.03
        nrefs    = 1860.0
        oid      = @2900336
        oname    = NAME VEGA
        otype    = V*
        plx.e    = 0.55
        plx.q    = A
        plx.r    = [u'1997A', u'&', u'A...323L..49P']
        plx.v    = 128.93
        pm.e     = 0.83
        pm.epmDE = 0.6
        pm.epmRA = 0.57
        pm.pa    = 35.0
        pm.pmDE  = 287.47
        pm.pmRA  = 201.03
        pm.q     = A
        pm.r     = [u'1997A', u'&', u'A...323L..49P']
        pm.v     = 350.79
        refPos   = [u'1997A', u'&', u'A...323L..49P']
        spNum    = 0.0000C800.0030.0000000000000000
        spType   = A0V


    >>> info = search('vega',db='N')
    >>> for key1 in sorted(info.keys()):
    ...    print ('%s = %s'%(key1.ljust(8),info[key1]))
    INFO     = from cache
    alias    = [u'alpha Lyr', u'HR 7001', u'HD 172167', u'IRAS  18352+3844', u'IRAS F18352+3844']
    errDEmas = 4824.0
    errRAmas = 19570.0
    jdedeg   = 38.782316
    jpos     = 18:36:55.70 +38:46:56.3
    jradeg   = 279.2321017
    oname    = VEGA
    otype    = !*
    refPos   = 1990IRASF.C...0000M

    @param ID: name of the source
    @type ID: str
    @param db: database to use
    @type db: str ('N','S','V','A')
    @return: (nested) dictionary containing information on star
    @rtype: dictionary
    """
    base_url = get_URI(ID, db=db)
    with urllib.request.urlopen(base_url) as ff:
        xmlpage = ""
        for line in ff.readlines():
            line = line.decode('utf-8')
            line_ = line[::-1].strip(' ')[::-1]
            if line_[0] == '<':
                line = line_
            xmlpage += line.strip('\n')
        database = xmlparser.XMLParser(xmlpage).content
        try:
            database = database['Sesame']['Target']['%s' % (db)]['Resolver']
            database = database[list(database.keys())[0]]
        except KeyError as IndexError:
            # -- we found nothing!
            database = {}

    if fix:
        # -- fix the parallax: make sure we have the Van Leeuwen 2007 value.
        #   simbad seems to have changed to old values to the new ones
        # somewhere in 2011. We check if this is the case for all stars:
        if 'plx' in database and not ('2007' in database['plx']['r']):
            data, units, comms = vizier.search('I/311/hip2', ID=ID)
            if data is not None and len(data):
                if 'plx' not in database:
                    database['plx'] = {}
                database['plx']['v'] = data['Plx'][0]
                database['plx']['e'] = data['e_Plx'][0]
                database['plx']['r'] = 'I/311/hip2'
        # -- fix the spectral type
        data, units, comms = vizier.search('B/mk/mktypes', ID=ID)
        if data is not None and len(data):
            database['spType'] = data['SpType'][0]
        if 'jpos' in database:
            # -- add galactic coordinates (in degrees)
            ra, dec = database['jpos'].split()
            gal = conversions.convert('equatorial',
                                      'galactic', (str(ra), str(dec)),
                                      epoch='2000')
            gal = float(gal[0]) / np.pi * 180, float(gal[1]) / np.pi * 180
            database['galpos'] = gal
        # -- fix the proper motions
        data, units, comms = vizier.search('I/317/sample', ID=ID)
        if data is not None and len(data):
            if 'pm' not in database:
                database['pm'] = {}
            database['pm']['pmRA'] = data['pmRA'][0]
            database['pm']['pmDE'] = data['pmDE'][0]
            database['pm']['epmRA'] = data['e_pmRA'][0]
            database['pm']['epmDE'] = data['e_pmDE'][0]
            database['pm']['r'] = 'I/317/sample'
    return database
def read_corot(fits_file, return_header=False, type_data="hel", remove_flagged=True):
    """
    Read CoRoT data from a CoRoT FITS file.
    
    Both SISMO and EXO data are recognised and extracted accordingly.
    
    type_data is one of:
        - type_data='raw'
        - type_data='hel': heliocentric unequidistant
        - type_data='helreg': heliocentric equidistant
    
    @param fits_file: CoRoT FITS file name
    @type fits_file: string
    @param return_header: return header information as dictionary
    @type return_header: bool
    @param type_data: type of data to return
    @type type_data: string (one of 'raw','hel' or 'helreg')
    @param remove_flagged: remove flagged datapoints
    @type remove_flagged: True
    @return: CoRoT data (times, flux, error, flags)
    @rtype: array, array, array, array(, header)
    """
    # -- read in the FITS file
    # headers: ['DATE', 'DATEJD', 'DATEHEL', 'STATUS', 'WHITEFLUX', 'WHITEFLUXDEV', 'BG', 'CORREC']
    fits_file_ = pyfits.open(fits_file)
    if fits_file_[0].header["hlfccdid"][0] == "A":
        times, flux, error, flags = (
            fits_file_[type_data].data.field(0),
            fits_file_[type_data].data.field(1),
            fits_file_[type_data].data.field(2),
            fits_file_[type_data].data.field(3),
        )
        # extract the header if asked
        if return_header:
            header = fits_file_[0].header
        fits_file_.close()

        logger.debug("Read CoRoT SISMO file %s" % (fits_file))
    elif fits_file_[0].header["hlfccdid"][0] == "E":
        times = fits_file_["bintable"].data.field("datehel")
        if "blueflux" in fits_file_["bintable"].columns.names:
            blueflux, e_blueflux = (
                fits_file_["bintable"].data.field("blueflux"),
                fits_file_["bintable"].data.field("bluefluxdev"),
            )
            greenflux, e_greenflux = (
                fits_file_["bintable"].data.field("greenflux"),
                fits_file_["bintable"].data.field("greenfluxdev"),
            )
            redflux, e_redflux = (
                fits_file_["bintable"].data.field("redflux"),
                fits_file_["bintable"].data.field("redfluxdev"),
            )
            # -- chromatic light curves
            if type_data == "colors":
                flux = np.column_stack([blueflux, greenflux, redflux])
                error = np.column_stack([e_blueflux, e_greenflux, e_redflux]).min(axis=1)
            # -- white light curves
            else:
                flux = blueflux + greenflux + redflux
                error = np.sqrt(e_blueflux ** 2 + e_greenflux ** 2 + e_redflux ** 2)
        else:
            flux, error = (
                fits_file_["bintable"].data.field("whiteflux"),
                fits_file_["bintable"].data.field("whitefluxdev"),
            )
        flags = fits_file_["bintable"].data.field("status")

    # remove flagged datapoints if asked
    if remove_flagged:
        keep1 = flags == 0
        keep2 = error != -1
        logger.info(
            "Remove: flagged (%d) no valid error (%d) datapoints (%d)"
            % (len(keep1) - keep1.sum(), len(keep2) - keep2.sum(), len(keep1))
        )
        keep = keep1 & keep2
        times, flux, error, flags = times[keep], flux[keep], error[keep], flags[keep]

    # convert times to heliocentric JD
    times = conversions.convert("MJD", "JD", times, jtype="corot")

    if return_header:
        return times, flux, error, flags, header
    else:
        return times, flux, error, flags
Beispiel #35
0
def get_photometry(ID=None,extra_fields=['dist','ra','dec'],**kwargs):
    """
    Download all available photometry from a star to a record array.

    For extra kwargs, see L{_get_URI} and L{gator2phot}

    Example usage:

    >>> import pylab
    >>> import vizier
    >>> name = 'kr cam'
    >>> master = vizier.get_photometry(name,to_units='erg/s/cm2/AA',extra_fields=[])
    >>> master = get_photometry(name,to_units='erg/s/cm2/AA',extra_fields=[],master=master)
    >>> p = pylab.figure()
    >>> wise = np.array(['WISE' in photband and True or False for photband in master['photband']])
    >>> p = pylab.errorbar(master['cwave'],master['cmeas'],yerr=master['e_cmeas'],fmt='ko')
    >>> p = pylab.errorbar(master['cwave'][wise],master['cmeas'][wise],yerr=master['e_cmeas'][wise],fmt='ro',ms=8)
    >>> p = pylab.gca().set_xscale('log')
    >>> p = pylab.gca().set_yscale('log')
    >>> p = pylab.show()

    Other examples:
    >>> master = get_photometry(ra=71.239527,dec=-70.589427,to_units='erg/s/cm2/AA',extra_fields=[],radius=1.)
    >>> master = get_photometry(ID='J044458.39-703522.6',to_units='W/m2',extra_fields=[],radius=1.)
    """
    kwargs['ID'] = ID
    to_units = kwargs.pop('to_units','erg/s/cm2/AA')
    master_ = kwargs.get('master',None)
    master = None
    #-- retrieve all measurements
    for source in cat_info.sections():
        results,units,comms = search(source,**kwargs)
        if results is not None:
            master = gator2phot(source,results,units,master,extra_fields=extra_fields)

    #-- convert the measurement to a common unit.
    if to_units and master is not None:
        #-- prepare columns to extend to basic master
        dtypes = [('cwave','f8'),('cmeas','f8'),('e_cmeas','f8'),('cunit','U50')]
        cols = [[],[],[],[]]
        #-- forget about 'nan' errors for the moment
        no_errors = np.isnan(master['e_meas'])
        master['e_meas'][no_errors] = 0.
        #-- extend basic master
        zp = filters.get_info(master['photband'])
        for i in range(len(master)):
            try:
                value,e_value = conversions.convert(master['unit'][i],to_units,master['meas'][i],master['e_meas'][i],photband=master['photband'][i])
            except ValueError: # calibrations not available
                value,e_value = np.nan,np.nan
            except AssertionError: # the error or flux must be positive number
                value,e_value = np.nan,np.nan
            try:
                eff_wave = filters.eff_wave(master['photband'][i])
            except IOError:
                eff_wave = np.nan
            cols[0].append(eff_wave)
            cols[1].append(value)
            cols[2].append(e_value)
            cols[3].append(to_units)
        master = numpy_ext.recarr_addcols(master,cols,dtypes)
        #-- reset errors
        master['e_meas'][no_errors] = np.nan
        master['e_cmeas'][no_errors] = np.nan

    if master_ is not None and master is not None:
        master = numpy_ext.recarr_addrows(master_,master.tolist())
    elif master_ is not None:
        master = master_

    #-- and return the results
    return master
def vsini(wave,flux,epsilon=0.6,clam=None,window=None,**kwargs):
    """
    Deterimine vsini of an observed spectrum via the Fourier transform method.
    
    According to Simon-Diaz (2006) and Carroll (1933):
    
    vsini = 0.660 * c/ (lambda * f1)
    
    But more general (see Reiners 2001, Dravins 1990)
    
    vsini = q1 * c/ (lambda*f1)
    
    where f1 is the first minimum of the Fourier transform.
    
    The error is estimated as the Rayleigh limit of the Fourier Transform
    
    Example usage and tests: Generate some data. We need a central wavelength (A),
    the speed of light in angstrom/s, limb darkening coefficients and test
    vsinis:
    
    >>> clam = 4480.
    >>> c = conversions.convert('m/s','A/s',constants.cc)
    >>> epsilons = np.linspace(0.,1.0,10)
    >>> vsinis = np.linspace(50,300,10)
    
    We analytically compute the shape of the Fourier transform in the following
    domain (and need the C{scipy.special.j1} for this)
    
    >>> x = np.linspace(0,30,1000)[1:]
    >>> from scipy.special import j1
    
    Keep track of the calculated and predicted q1 values:
    
    >>> q1s = np.zeros((len(epsilons),len(vsinis)))
    >>> q1s_pred = np.zeros((len(epsilons),len(vsinis)))
    
    Start a figure and set the color cycle
    
    >>> p= pl.figure()
    >>> p=pl.subplot(131)
    >>> color_cycle = [pl.cm.spectral(j) for j in np.linspace(0, 1.0, len(epsilons))]
    >>> p = pl.gca().set_color_cycle(color_cycle)
    >>> p=pl.subplot(133);p=pl.title('Broadening kernel')
    >>> p = pl.gca().set_color_cycle(color_cycle)
    
    Now run over all epsilons and vsinis and determine the q1 constant:
    
    >>> for j,epsilon in enumerate(epsilons):
    ...    for i,vsini in enumerate(vsinis):
    ...       vsini = conversions.convert('km/s','A/s',vsini)
    ...       delta = clam*vsini/c
    ...       lambdas = np.linspace(-5.,+5.,20000)
    ...       #-- analytical rotational broadening kernel
    ...       y = 1-(lambdas/delta)**2
    ...       G = (2*(1-epsilon)*np.sqrt(y)+pi*epsilon/2.*y)/(pi*delta*(1-epsilon/3))
    ...       lambdas = lambdas[-np.isnan(G)]
    ...       G = G[-np.isnan(G)]
    ...       G /= max(G)
    ...       #-- analytical FT of rotational broadening kernel
    ...       g = 2. / (x*(1-epsilon/3.)) * ( (1-epsilon)*j1(x) +  epsilon* (sin(x)/x**2 - cos(x)/x))
    ...       #-- numerical FT of rotational broadening kernel
    ...       sigma,g_ = pergrams.deeming(lambdas-lambdas[0],G,fn=2e0,df=1e-3,norm='power')
    ...       myx = 2*pi*delta*sigma
    ...       #-- get the minima
    ...       rise = np.diff(g_[1:])>=0
    ...       fall = np.diff(g_[:-1])<=0
    ...       minima = sigma[1:-1][rise & fall]
    ...       minvals = g_[1:-1][rise & fall]
    ...       q1s[j,i] =  vsini / (c/clam/minima[0])
    ...       q1s_pred[j,i] = 0.610 + 0.062*epsilon + 0.027*epsilon**2 + 0.012*epsilon**3 + 0.004*epsilon**4
    ...    p= pl.subplot(131)
    ...    p= pl.plot(vsinis,q1s[j],'o',label='$\epsilon$=%.2f'%(epsilon));pl.gca()._get_lines.count -= 1
    ...    p= pl.plot(vsinis,q1s_pred[j],'-')
    ...    p= pl.subplot(133)
    ...    p= pl.plot(lambdas,G,'-')
    
    And plot the results:
    
    >>> p= pl.subplot(131)
    >>> p= pl.xlabel('v sini [km/s]');p = pl.ylabel('q1')
    >>> p= pl.legend(prop=dict(size='small'))
    
    
    >>> p= pl.subplot(132);p=pl.title('Fourier transform')
    >>> p= pl.plot(x,g**2,'k-',label='Analytical FT')
    >>> p= pl.plot(myx,g_/max(g_),'r-',label='Computed FT')
    >>> p= pl.plot(minima*2*pi*delta,minvals/max(g_),'bo',label='Minima')
    >>> p= pl.legend(prop=dict(size='small'))
    >>> p= pl.gca().set_yscale('log')
    
    ]include figure]]ivs_spectra_tools_vsini_kernel.png]
    
    Extra keyword arguments are passed to L{pergrams.deeming}
    
    @param wave: wavelength array in Angstrom
    @type wave: ndarray
    @param flux: normalised flux of the profile
    @type flux: ndarray
    @rtype: (array,array),(array,array),array,float
    @return: periodogram (freqs in s/km), extrema (weird units), vsini values (km/s), error (km/s)
    """
    cc = conversions.convert('m/s','AA/s',constants.cc)
    #-- clip the wavelength and flux region if needed:
    if window is not None:
        keep = (window[0]<=wave) & (wave<=window[1])
        wave,flux = wave[keep],flux[keep]
    #-- what is the central wavelength? If not given, it's the middle of the
    #   wavelength range
    if clam is None: clam = ((wave[0]+wave[-1])/2)
    #-- take care of the limb darkening
    q1 = 0.610 + 0.062*epsilon + 0.027*epsilon**2 + 0.012*epsilon**3 + 0.004*epsilon**4
    #-- do the Fourier transform and normalise
    #flux = flux / (np.median(np.sort(flux)[-5:]))
    freqs,ampls = pergrams.deeming(wave,(1-flux),**kwargs)
    ampls = ampls/max(ampls)
    error = 1./wave.ptp()
    #-- get all the peaks
    rise = np.diff(ampls[1:])>=0
    fall = np.diff(ampls[:-1])<=0
    minima = freqs[1:-1][rise & fall]
    minvals = ampls[1:-1][rise & fall]
    #-- compute the vsini and convert to km/s
    freqs = freqs*clam/q1/cc
    freqs = conversions.convert('s/AA','s/km',freqs,wave=(clam,'AA'))
    vsini_values = cc/clam*q1/minima
    vsini_values = conversions.convert('AA/s','km/s',vsini_values)#,wave=(clam,'AA'))
    #-- determine the error as the rayleigh limit
    error = error*clam/q1/cc
    error = conversions.convert('s/AA','s/km',error,wave=(clam,'AA'))
    return (freqs,ampls),(minima,minvals),vsini_values,error
def get_photometry(ID=None, extra_fields=[], **kwargs):
    """
    Download all available photometry from a star to a record array.

    Extra fields will not be useful probably.

    For extra kwargs, see L{_get_URI} and L{gcpd2phot}
    """
    to_units = kwargs.pop('to_units', 'erg/s/cm2/AA')
    master_ = kwargs.get('master', None)
    master = None
    #-- retrieve all measurements
    for source in cat_info.sections():
        results, units, comms = search(source, ID=ID, **kwargs)
        if results is not None:
            master = gcpd2phot(source,
                               results,
                               units,
                               master,
                               extra_fields=extra_fields)

    #-- convert the measurement to a common unit.
    if to_units and master is not None:
        #-- prepare columns to extend to basic master
        dtypes = [('cwave', 'f8'), ('cmeas', 'f8'), ('e_cmeas', 'f8'),
                  ('cunit', 'U50')]
        cols = [[], [], [], []]
        #-- forget about 'nan' errors for the moment
        no_errors = np.isnan(master['e_meas'])
        master['e_meas'][no_errors] = 0.
        #-- extend basic master
        try:
            zp = filters.get_info(master['photband'])
        except:
            print(master['photband'])
            raise
        for i in range(len(master)):
            to_units_ = to_units + ''
            try:
                value, e_value = conversions.convert(
                    master['unit'][i],
                    to_units,
                    master['meas'][i],
                    master['e_meas'][i],
                    photband=master['photband'][i])
            except ValueError:  # calibrations not available
                # if it is a magnitude color, try converting it to a flux ratio
                if 'mag' in master['unit'][i]:
                    try:
                        value, e_value = conversions.convert(
                            'mag_color',
                            'flux_ratio',
                            master['meas'][i],
                            master['e_meas'][i],
                            photband=master['photband'][i])
                        to_units_ = 'flux_ratio'
                    except ValueError:
                        value, e_value = np.nan, np.nan
                # else, we are powerless...
                else:
                    value, e_value = np.nan, np.nan
            try:
                eff_wave = filters.eff_wave(master['photband'][i])
            except IOError:
                eff_wave = np.nan
            cols[0].append(eff_wave)
            cols[1].append(value)
            cols[2].append(e_value)
            cols[3].append(to_units_)
        master = numpy_ext.recarr_addcols(master, cols, dtypes)
        #-- reset errors
        master['e_meas'][no_errors] = np.nan
        master['e_cmeas'][no_errors] = np.nan

    #-- if a master is given as a keyword, and data is found in this module,
    #   append the two
    if master_ is not None and master is not None:
        master = numpy_ext.recarr_addrows(master_, master.tolist())
    elif master is None:
        master = master_
    #-- and return the results
    return master
Beispiel #38
0
def standardize(star,
                _from,
                info='local',
                units='cgs',
                add_center_point=False):
    """
    Standardize units and naming of a stellar model.
    
    @param star: information to standardize
    @type star: dict or rec array
    @param _from: name of the type of file (mesa, cles...)
    @type _from: str
    @param info: is this local or global information?
    @type info: str
    @param units: units to work with
    @type units: str
    @param add_center_point: if True, a center point will be added
    @type add_center_point: bool
    @return: standardized star
    @rtype: dict or recarray
    """
    basedir = os.path.dirname(__file__)
    if _from == 'mesa' and info == 'global' and not hasattr(star, 'keys'):
        #-- convert global recarray into dictionary
        star_ = {}
        for name in star.dtype.names:
            star_[name] = star[name][0]
        star = star_

    if hasattr(star, 'keys'):
        keys = star.keys()
    else:
        keys = star.dtype.names

    if _from is not None:
        mesa_onames, mesa_names, mesa_units = read_cols(
            os.path.join(basedir, 'mesa_{0}.cols'.format(info)))
        mnames, onames, ounits = read_cols(
            os.path.join(basedir, _from + '_{0}.cols'.format(info)))

        new_star = {}
        for key in keys:
            #-- maybe we cannot convert the column (yet):
            try:
                index = onames.index(key)
            except ValueError:
                logger.debug(
                    'Cannot convert %s from (%s,%s) to mesa name (skipping)' %
                    (key, _from, info))
                continue
            #-- maybe information is not in 'standard' list. If it is, convert to
            #   standard units and standard name. Otherwise, just change the name
            try:
                if ounits[index] == 'ignore':
                    continue
                mindex = mesa_names.index(mnames[index])
                if ounits[index] == 'n' and mesa_units[mindex] == 'n kg':
                    value = star[key] * getattr(constants,
                                                mnames[index].title())
                elif mesa_units[mindex] or ounits[index]:
                    value = conversions.convert(ounits[index], units,
                                                star[key])
                else:
                    value = star[key]
            except ValueError:
                #logger.warning('Cannot convert %s to mesa units (keeping original units)'%(key))
                value = star[key]

            new_star[mnames[index]] = value
        #-- if input was not a dictionary, convert it to a record array
        if not hasattr(star, 'keys'):
            names = new_star.keys()
            data = [new_star[name] for name in names]
            new_star = np.rec.fromarrays(data, names=names)
            keys = names
        else:
            keys = star.keys()
    else:
        if not hasattr(star, 'keys'):
            names = star.dtype.names
            data = [star[name] for name in names]
            new_star = np.rec.fromarrays(data, names=names)
            keys = list(names)
        else:
            new_star = star
            keys = star.keys()

    #-- some profiles start at the surface...
    if info == 'local' and _from in ['mesa']:
        new_star = new_star[::-1]
        add_center_point = True
        logger.info('Reversed profile (first point is now center)')
    #-- some profiles have no inner point
    if add_center_point:
        if info == 'global':
            raise ValueError, 'cannot add center point in global variables'
        new_star = np.hstack([new_star[:1], new_star])
        #-- fill-in some initial values
        for key in ['mass', 'q', 'radius']:
            if key in keys:
                new_star[key][0] = 0.
        logger.info('Added center point')
    #-- sometimes it is easy to precompute additional information that can be
    #   derived from the other information available. We might need this
    #   information for pulsation analysis, profile studies etc...
    #-- (surface) gravity
    if not 'g' in keys:
        if info == 'local' and 'mass' in keys and 'radius' in keys:
            g = constants.GG * new_star['mass'] / new_star['radius']**2
            g[0] = 0.
            new_star = pl.mlab.rec_append_fields(new_star, ['g'], [g])
        elif info == 'global' and 'star_mass' in keys and 'photosphere_r' in keys:
            new_star['g'] = constants.GG * new_star['star_mass'] / new_star[
                'photosphere_r']**2
        logger.info('Derived g from model info (%s, %s)' % (_from, info))
    #-- dP/dr
    if info == 'local' and not 'dP_dr' in keys and 'Rho' in keys and 'mass' in keys and 'radius' in keys:
        dP_dr = -new_star['Rho'] * constants.GG * new_star['mass'] / new_star[
            'radius']**2  #dP/dr=-rho g
        dP_dr[0] = 0
        new_star = pl.mlab.rec_append_fields(new_star, ['dP_dr'], [dP_dr])
        logger.info('Derived dP_dr from model info (%s, %s)' % (_from, info))
        keys.append('dP_dr')
    #-- Brunt A
    if info == 'local' and not 'brunt_A' in keys and 'brunt_N2' in keys:
        A = new_star['brunt_N2'] * new_star['radius'] / new_star['g']
        new_star = pl.mlab.rec_append_fields(new_star, ['brunt_A'], [A])
        logger.warning('Brunt A via brunt N2')
        keys.append('brunt_A')
    if info=='local' and not 'brunt_A' in keys and 'radius' in keys and 'Rho' in keys \
        and 'gamma1' in keys and 'pressure' in keys and 'dP_dr' in keys:
        r, rho, G1, P = new_star['radius'], new_star['Rho'], new_star[
            'gamma1'], new_star['pressure']
        dP_dr = new_star['dP_dr']
        x = new_star['mass'] / new_star['mass'][-1]
        A = -ne.deriv(r, rho) / rho * r + 1. / G1 * dP_dr / P * r
        N2 = new_star['g'] * A / r
        N2[0] = 0
        A[:2] = np.polyval(np.polyfit(x[2:5]**2, A[2:5], 2), x[:2]**2)
        new_star = pl.mlab.rec_append_fields(new_star, ['brunt_A', 'brunt_N2'],
                                             [A, N2])
        logger.warning('Brunt A/N via explicit derivatives')
        logger.info('Extrapolated inner two points of Brunt A')
    #-- override brunt_N2_dimensionless:
    if info=='local' and 'grad_density' in keys and 'radius' in keys and 'Rho' in keys \
        and 'gamma1' in keys and 'pressure' in keys and 'mass' in keys:
        g, rho, P, G1 = new_star['g'], new_star['Rho'], new_star[
            'pressure'], new_star['gamma1']
        dlnrho_dlnP = new_star['grad_density']
        N2 = rho * g**2 / P * (-1. / G1 + dlnrho_dlnP)
        #factor = 3*constants.GG*new_star['mass'].max()/new_star['radius'].max()**3
        #new_star['brunt_N2_dimensionless'] = N2/factor
        x = new_star['mass'] / new_star['mass'][-1]
        A = new_star['radius'] * N2 / g
        A[:2] = np.polyval(np.polyfit(x[2:5]**2, A[2:5], 2), x[:2]**2)
        new_star['brunt_A'] = A
        if not 'brunt_N2' in new_star.dtype.names:
            new_star = pl.mlab.rec_append_fields(new_star, ['brunt_N2'], [N2])
        else:
            new_star['brunt_N2'] = N2
        logger.info('Brunt A via precomputed partial derivatives (%s,%s)' %
                    (_from, info))
        logger.info('Extrapolated inner two points of Brunt A')
    #-- ADIPLS quantity Vg
    if info=='local' and not 'Vg' in keys and 'gamma1' in keys and 'radius' in keys and 'Rho' in keys \
        and 'gamma1' in keys and 'pressure' in keys and 'mass' in keys:
        m, rho = new_star['mass'], new_star['Rho']
        G1, P, r = new_star['gamma1'], new_star['pressure'], new_star['radius']
        Vg = constants.GG * m * rho / (G1 * P * r)
        Vg[0] = 0.
        new_star = pl.mlab.rec_append_fields(new_star, ['Vg'], [Vg])
    elif info == 'local' and not 'Vg' in keys and not 'gamma1' in keys:
        logger.warning('Cannot compute Vg because gamma1 is not available')
    #-- ADIPLS quantitiy U
    if info=='local' and not 'U' in keys and 'gamma1' in keys and 'radius' in keys and 'Rho' in keys \
        and 'gamma1' in keys and 'pressure' in keys and 'mass' in keys:
        rho, m, r = new_star['Rho'], new_star['mass'], new_star['radius']
        U = 4 * np.pi * rho * r**3 / m
        x = new_star['mass'] / new_star['mass'][-1]
        U[0] = np.polyval(np.polyfit(x[1:4], U[1:4], 2), x[0])
        new_star = pl.mlab.rec_append_fields(new_star, ['U'], [U])
    #-- dynamical time scale
    if info == 'global' and not 'dynamic_time' in keys and 'photosphere_r' in keys and 'star_mass' in keys:
        R = new_star['photosphere_r']
        M = new_star['star_mass']
        new_star['dynamic_time'] = np.sqrt(R**3 / (constants.GG * M))
        logger.info('Derived dynamic_time from model info (%s, %s)' %
                    (_from, info))
    if info=='local' and not 'csound' in keys and 'gamma1' in keys and 'radius' in keys and 'Rho' in keys \
        and 'gamma1' in keys and 'pressure' in keys and 'mass' in keys:
        csound = np.sqrt(new_star['gamma1'] * new_star['pressure'] /
                         new_star['Rho'])
        new_star = pl.mlab.rec_append_fields(new_star, ['csound'], [csound])
    #-- some profiles don't have a center value
    if _from in []:
        pass

    #-- that's it!
    return new_star
def standardize(star,_from,info='local',units='cgs',add_center_point=False):
    """
    Standardize units and naming of a stellar model.
    
    @param star: information to standardize
    @type star: dict or rec array
    @param _from: name of the type of file (mesa, cles...)
    @type _from: str
    @param info: is this local or global information?
    @type info: str
    @param units: units to work with
    @type units: str
    @param add_center_point: if True, a center point will be added
    @type add_center_point: bool
    @return: standardized star
    @rtype: dict or recarray
    """
    basedir = os.path.dirname(__file__)
    if _from=='mesa' and info=='global' and not hasattr(star,'keys'):
        #-- convert global recarray into dictionary
        star_ = {}
        for name in star.dtype.names:
            star_[name] = star[name][0]
        star = star_
    
    if hasattr(star,'keys'):
        keys = star.keys()
    else:
        keys = star.dtype.names
    
    if _from is not None:
        mesa_onames,mesa_names,mesa_units = read_cols(os.path.join(basedir,'mesa_{0}.cols'.format(info)))
        mnames,onames,ounits = read_cols(os.path.join(basedir,_from+'_{0}.cols'.format(info)))        
        
        new_star = {}
        for key in keys:
            #-- maybe we cannot convert the column (yet):
            try:
                index = onames.index(key)
            except ValueError:
                logger.debug('Cannot convert %s from (%s,%s) to mesa name (skipping)'%(key,_from,info))
                continue
            #-- maybe information is not in 'standard' list. If it is, convert to
            #   standard units and standard name. Otherwise, just change the name
            try:
                if ounits[index]=='ignore':
                    continue
                mindex = mesa_names.index(mnames[index])
                if ounits[index]=='n' and mesa_units[mindex]=='n kg':
                    value = star[key]*getattr(constants,mnames[index].title())
                elif mesa_units[mindex] or ounits[index]:
                    value = conversions.convert(ounits[index],units,star[key])
                else:
                    value = star[key]
            except ValueError:
                #logger.warning('Cannot convert %s to mesa units (keeping original units)'%(key))
                value = star[key]
            
            new_star[mnames[index]] = value
        #-- if input was not a dictionary, convert it to a record array
        if not hasattr(star,'keys'):
            names = new_star.keys()
            data = [new_star[name] for name in names]
            new_star = np.rec.fromarrays(data,names=names)
            keys = names
        else:
            keys = star.keys()
    else:
        if not hasattr(star,'keys'):
            names = star.dtype.names
            data = [star[name] for name in names]
            new_star = np.rec.fromarrays(data,names=names)
            keys = list(names)
        else:
            new_star = star
            keys = star.keys()
    
    
    
    #-- some profiles start at the surface...
    if info=='local' and _from in ['mesa']:
        new_star = new_star[::-1]
        add_center_point = True
        logger.info('Reversed profile (first point is now center)')
    #-- some profiles have no inner point
    if add_center_point:
        if info=='global':
            raise ValueError,'cannot add center point in global variables'
        new_star = np.hstack([new_star[:1],new_star])
        #-- fill-in some initial values
        for key in ['mass','q','radius']:
            if key in keys:
                new_star[key][0] = 0.
        logger.info('Added center point')
    #-- sometimes it is easy to precompute additional information that can be
    #   derived from the other information available. We might need this
    #   information for pulsation analysis, profile studies etc...
    #-- (surface) gravity
    if not 'g' in keys:
        if info=='local' and 'mass' in keys and 'radius' in keys:
            g = constants.GG*new_star['mass']/new_star['radius']**2
            g[0] = 0.
            new_star = pl.mlab.rec_append_fields(new_star,['g'],[g])
        elif info=='global' and 'star_mass' in keys and 'photosphere_r' in keys:
            new_star['g'] = constants.GG*new_star['star_mass']/new_star['photosphere_r']**2
        logger.info('Derived g from model info (%s, %s)'%(_from,info))
    #-- dP/dr
    if info=='local' and not 'dP_dr' in keys and 'Rho' in keys and 'mass' in keys and 'radius' in keys:
        dP_dr = -new_star['Rho']*constants.GG*new_star['mass']/new_star['radius']**2 #dP/dr=-rho g
        dP_dr[0] = 0
        new_star = pl.mlab.rec_append_fields(new_star,['dP_dr'],[dP_dr])
        logger.info('Derived dP_dr from model info (%s, %s)'%(_from,info))
        keys.append('dP_dr')
    #-- Brunt A
    if info=='local' and not 'brunt_A' in keys and 'brunt_N2' in keys:
        A = new_star['brunt_N2']*new_star['radius']/new_star['g']
        new_star = pl.mlab.rec_append_fields(new_star,['brunt_A'],[A])
        logger.warning('Brunt A via brunt N2')
        keys.append('brunt_A')
    if info=='local' and not 'brunt_A' in keys and 'radius' in keys and 'Rho' in keys \
        and 'gamma1' in keys and 'pressure' in keys and 'dP_dr' in keys:
        r,rho,G1,P = new_star['radius'],new_star['Rho'],new_star['gamma1'],new_star['pressure']
        dP_dr = new_star['dP_dr']
        x = new_star['mass']/new_star['mass'][-1]
        A = -ne.deriv(r,rho)/rho*r + 1./G1 * dP_dr/P*r
        N2 = new_star['g']*A/r
        N2[0] = 0
        A[:2] = np.polyval(np.polyfit(x[2:5]**2,A[2:5],2),x[:2]**2)
        new_star = pl.mlab.rec_append_fields(new_star,['brunt_A','brunt_N2'],[A,N2])
        logger.warning('Brunt A/N via explicit derivatives')
        logger.info('Extrapolated inner two points of Brunt A')
    #-- override brunt_N2_dimensionless:
    if info=='local' and 'grad_density' in keys and 'radius' in keys and 'Rho' in keys \
        and 'gamma1' in keys and 'pressure' in keys and 'mass' in keys:
        g,rho,P,G1 = new_star['g'],new_star['Rho'],new_star['pressure'],new_star['gamma1']
        dlnrho_dlnP = new_star['grad_density']
        N2 = rho*g**2/P*(-1./G1 + dlnrho_dlnP)
        #factor = 3*constants.GG*new_star['mass'].max()/new_star['radius'].max()**3
        #new_star['brunt_N2_dimensionless'] = N2/factor
        x = new_star['mass']/new_star['mass'][-1]
        A = new_star['radius']*N2/g
        A[:2] = np.polyval(np.polyfit(x[2:5]**2,A[2:5],2),x[:2]**2)
        new_star['brunt_A'] = A
        if not 'brunt_N2' in new_star.dtype.names:
            new_star = pl.mlab.rec_append_fields(new_star,['brunt_N2'],[N2])
        else:
            new_star['brunt_N2'] = N2
        logger.info('Brunt A via precomputed partial derivatives (%s,%s)'%(_from,info))
        logger.info('Extrapolated inner two points of Brunt A')
    #-- ADIPLS quantity Vg
    if info=='local' and not 'Vg' in keys and 'gamma1' in keys and 'radius' in keys and 'Rho' in keys \
        and 'gamma1' in keys and 'pressure' in keys and 'mass' in keys:
        m,rho = new_star['mass'],new_star['Rho']
        G1,P,r = new_star['gamma1'],new_star['pressure'],new_star['radius']
        Vg = constants.GG*m*rho/(G1*P*r)
        Vg[0] = 0.
        new_star = pl.mlab.rec_append_fields(new_star,['Vg'],[Vg])
    elif info=='local' and not 'Vg' in keys and not 'gamma1' in keys:
        logger.warning('Cannot compute Vg because gamma1 is not available')
    #-- ADIPLS quantitiy U
    if info=='local' and not 'U' in keys and 'gamma1' in keys and 'radius' in keys and 'Rho' in keys \
        and 'gamma1' in keys and 'pressure' in keys and 'mass' in keys:
        rho,m,r = new_star['Rho'],new_star['mass'],new_star['radius']
        U = 4*np.pi*rho*r**3/m
        x = new_star['mass']/new_star['mass'][-1]
        U[0] = np.polyval(np.polyfit(x[1:4],U[1:4],2),x[0])
        new_star = pl.mlab.rec_append_fields(new_star,['U'],[U])
    #-- dynamical time scale
    if info=='global' and not 'dynamic_time' in keys and 'photosphere_r' in keys and 'star_mass' in keys:
        R = new_star['photosphere_r']
        M = new_star['star_mass']
        new_star['dynamic_time'] = np.sqrt(R**3/(constants.GG*M))
        logger.info('Derived dynamic_time from model info (%s, %s)'%(_from,info))
    if info=='local' and not 'csound' in keys and 'gamma1' in keys and 'radius' in keys and 'Rho' in keys \
        and 'gamma1' in keys and 'pressure' in keys and 'mass' in keys:
        csound = np.sqrt(new_star['gamma1']*new_star['pressure']/new_star['Rho'])
        new_star = pl.mlab.rec_append_fields(new_star,['csound'],[csound])
    #-- some profiles don't have a center value
    if _from in []:
        pass
    
    #-- that's it!
    return new_star
def helcorr(ra2000, dec2000, jd, obs_long=None, obs_lat=None, obs_alt=None, debug=False):
  """
  calculates heliocentric Julian date, baricentric and heliocentric radial
  velocity corrections from:

  INPUT:
  @param ra2000: Right ascension of object for epoch 2000.0 (hours)
  @type ra2000: float
  @param dec2000: declination of object for epoch 2000.0 (degrees)
  @type dec2000: float
  @param jd: julian date for the middle of exposure
  @type jd: float
  @keyword obs_long: longitude of observatory (degrees, western direction is positive)
  @type obs_long: float
  @keyword obs_lat: latitude of observatory (degrees)
  @type obs_lat: float
  @keyword obs_alt: altitude of observatory (meters)
  @type obs_alt: float
  
  Algorithms used are taken from the IRAF task noao.astutils.rvcorrect
  and some procedures of the IDL Astrolib are used as well.
  Accuracy is about 0.5 seconds in time and about 1 m/s in velocity.
  
  History:
  written by Peter Mittermayer, Nov 8,2003
  2005-January-13   Kudryavtsev   Made more accurate calculation of the sideral time.
                                  Conformity with MIDAS compute/barycorr is checked.
  2005-June-20      Kochukhov Included precession of RA2000 and DEC2000 to current epoch
  """
  _radeg = 180.0 / np.pi
  
  # If observatory not given, set La Palma
  if obs_long is None:
    obs_long = 17.878333
  if obs_lat is None:
    obs_lat = 28.762222 
  if obs_alt is None:
    obs_alt = 2333.
  
  #covert JD to Gregorian calendar date
  xjd = jd*1.0
  jd  = jd-2400000.0
  year, month, dayraw = convert("JD", "CD", xjd)
  day = dayraw-dayraw%24.
  ut  = 24.*(dayraw%24)
  
  #current epoch
  epoch = year + month / 12. + day / 365.
  
  #precess ra2000 and dec2000 to current epoch
  ra,dec=precess(ra2000*15., dec2000, 2000.0, epoch)
  
  #calculate heliocentric julian date
  hjd = np.array(helio_jd(jd, ra, dec)).astype(float)
  
  #DIURNAL VELOCITY (see IRAF task noao.astutil.rvcorrect)
  dlat = -(11. * 60. + 32.743) * np.sin(2 * obs_lat / _radeg) + 1.1633 * np.sin(4 * obs_lat / _radeg) - 0.0026 * np.sin(6 * obs_lat / _radeg)
  lat = obs_lat + dlat / 3600
  
  #calculate distance of observer from earth center
  r = 6378160.0 * (0.998327073 + 0.001676438 * np.cos(2 * lat / _radeg) - 0.00000351 * np.cos(4 * lat / _radeg) + 0.000000008 * np.cos(6 * lat / _radeg)) + obs_alt
  
  #calculate rotational velocity (perpendicular to the radius vector) in km/s
  #23.934469591229 is the siderial day in hours for 1986
  v = 2. * np.pi * (r / 1000.) / (23.934469591229 * 3600.)
  
  #calculating local mean siderial time (see astronomical almanach)
  tu = (jd - 51545.0) / 36525
  gmst = 6.697374558 + ut + (236.555367908 * (jd - 51545.0) + 0.093104 * tu ** 2 - 6.2e-6 * tu ** 3) / 3600
  lmst = (gmst - obs_long / 15) % 24
  
  #projection of rotational velocity along the line of sight
  vdiurnal = v * np.cos(lat / _radeg) * np.cos(dec / _radeg) * np.sin((ra - lmst * 15) / _radeg)
  
  #BARICENTRIC and HELIOCENTRIC VELOCITIES
  vh,vb=baryvel(xjd, 0)
  
  #project to line of sight
  vbar = vb[0] * np.cos(dec / _radeg) * np.cos(ra / _radeg) + vb[1] * np.cos(dec / _radeg) * np.sin(ra / _radeg) + vb[2] * np.sin(dec / _radeg)
  vhel = vh[0] * np.cos(dec / _radeg) * np.cos(ra / _radeg) + vh[1] * np.cos(dec / _radeg) * np.sin(ra / _radeg) + vh[2] * np.sin(dec / _radeg)
  corr = (vdiurnal + vbar) #using baricentric velocity for correction 
  
  return (corr, hjd)
def read_amdl(filename):
    """
    Read unformatted binary AMDL files from ADIPLS.
    
    @param filename: name of the file
    @type filename: str
    @return: global parameters, local parameters
    @rtype: dict, rec array
    """
    with open(filename,'rb') as ff:
        #-- read in all the bytes at once
        content = ff.read()
    #-- have a look at the header of the file
    header_fmt = '<iii'
    line_fmt = '<'+8*'d'
    head = struct.calcsize(header_fmt)
    line = struct.calcsize(line_fmt)
    header = content[:head]
    content = content[head:]
    print struct.unpack(header_fmt,header)
    model_name,icase,nn = struct.unpack(header_fmt,header)
    print filename,'model_name,nn',model_name,nn
    
    #-- read in the first line of the data. This contains
    #   global information on the model, and also the number
    #   of columns in the datafile.
    record = content[:line]
    content = content[line:]
    M,R,P_c,rho_c,D5,D6,mu,D8 = struct.unpack(line_fmt,record)
    
    #-- print out some information
    M_ = conversions.convert('g','Msol',M)
    R_ = conversions.convert('cm','Rsol',R)
    print "Model information: (%d meshpoints)"%(nn)
    print "  M=%.3gMsol, R=%.3gRsol"%(M_,R_)
    print "  P_c=%.3g g/cm/s2, rho_c=%.3g g/cm3"%(P_c,rho_c)
    print '  Polytropic index: %.2g'%(mu)
    print '  %.3g %3g'%(D5,D6)
    
    #-- derive the number of variables in this model file
    if D8>=100: nvars = 9
    elif D8>=10:nvars = 7
    else:       nvars = 6
    
    #-- then read in the rest, line by line
    line_fmt = '<'+nvars*'d'
    line = struct.calcsize(line_fmt)
    starl = []
    for i in range(nn):
        record = content[:line]
        content = content[line:]
        data = struct.unpack(line_fmt,record)
        starl.append(data)
    
    #-- end of the file
    line_fmt = '<i'
    record = content[:line]
    content = content[line:]
    data = struct.unpack(line_fmt,record)
    print 'endoffile',data
    
    #-- wrap up the local and global information on the star
    starl = np.rec.fromarrays(np.array(starl).T,names=['x','q/x3','Vg','G1','A','U'])
    starg = {'M':M,'R':R,'P_c':P_c,'rho_c':rho_c,
             'D5':D5,'D6':D6,'mu':mu}
    if len(content):
        raise ValueError,'uninterpreted data in file',fileanem
    
    return starg,starl
Beispiel #42
0
#===================================================================================
# Get all necessary data
#===================================================================================

#-- Get the reference flux
if reference == 'VEGA':
    #-- calculate Flam based on the Vega spectrum
    hdu = pyfits.open('alpha_lyr_stis_008.fits')
    wave, flux = hdu[1].data['wavelength'], hdu[1].data['flux']
    hdu.close()

else:
    #-- calculate Flam for the AB system
    wave = np.arange(3000, 9000, step=0.5)
    flux = cv.convert(cc.cc_units, 'AA/s', cc.cc) / wave**2 * 3631e-23

Flam_0 = model.synthetic_flux(wave, flux, photbands=photbands)

#-- load the calibrators
calibrators = ascii.read2array(basedir + 'calspec.ident',
                               splitchar=',',
                               dtype=str)


def get_synthetic_photometry(calibrator):
    """
   Integrate the spectrum belonging to this calibrator and return the synthetic magnitudes
   """

    hdu = pyfits.open(basedir + calibrator[1])
Beispiel #43
0
def vsini(wave, flux, epsilon=0.6, clam=None, window=None, **kwargs):
    """
    Deterimine vsini of an observed spectrum via the Fourier transform method.

    According to Simon-Diaz (2006) and Carroll (1933):

    vsini = 0.660 * c/ (lambda * f1)

    But more general (see Reiners 2001, Dravins 1990)

    vsini = q1 * c/ (lambda*f1)

    where f1 is the first minimum of the Fourier transform.

    The error is estimated as the Rayleigh limit of the Fourier Transform

    Example usage and tests: Generate some data. We need a central wavelength (A),
    the speed of light in angstrom/s, limb darkening coefficients and test
    vsinis:

    >>> clam = 4480.
    >>> c = conversions.convert('m/s','A/s',constants.cc)
    >>> epsilons = np.linspace(0.,1.0,10)
    >>> vsinis = np.linspace(50,300,10)

    We analytically compute the shape of the Fourier transform in the following
    domain (and need the C{scipy.special.j1} for this)

    >>> x = np.linspace(0,30,1000)[1:]
    >>> from scipy.special import j1

    Keep track of the calculated and predicted q1 values:

    >>> q1s = np.zeros((len(epsilons),len(vsinis)))
    >>> q1s_pred = np.zeros((len(epsilons),len(vsinis)))

    Start a figure and set the color cycle

    >>> p= pl.figure()
    >>> p=pl.subplot(131)
    >>> color_cycle = [pl.cm.spectral(j) for j in np.linspace(0, 1.0, len(epsilons))]
    >>> p = pl.gca().set_color_cycle(color_cycle)
    >>> p=pl.subplot(133);p=pl.title('Broadening kernel')
    >>> p = pl.gca().set_color_cycle(color_cycle)

    Now run over all epsilons and vsinis and determine the q1 constant:

    >>> for j,epsilon in enumerate(epsilons):
    ...    for i,vsini in enumerate(vsinis):
    ...       vsini = conversions.convert('km/s','A/s',vsini)
    ...       delta = clam*vsini/c
    ...       lambdas = np.linspace(-5.,+5.,20000)
    ...       #-- analytical rotational broadening kernel
    ...       y = 1-(lambdas/delta)**2
    ...       G = (2*(1-epsilon)*np.sqrt(y)+pi*epsilon/2.*y)/(pi*delta*(1-epsilon/3))
    ...       lambdas = lambdas[-np.isnan(G)]
    ...       G = G[-np.isnan(G)]
    ...       G /= max(G)
    ...       #-- analytical FT of rotational broadening kernel
    ...       g = 2. / (x*(1-epsilon/3.)) * ( (1-epsilon)*j1(x) +  epsilon* (sin(x)/x**2 - cos(x)/x))
    ...       #-- numerical FT of rotational broadening kernel
    ...       sigma,g_ = pergrams.deeming(lambdas-lambdas[0],G,fn=2e0,df=1e-3,norm='power')
    ...       myx = 2*pi*delta*sigma
    ...       #-- get the minima
    ...       rise = np.diff(g_[1:])>=0
    ...       fall = np.diff(g_[:-1])<=0
    ...       minima = sigma[1:-1][rise & fall]
    ...       minvals = g_[1:-1][rise & fall]
    ...       q1s[j,i] =  vsini / (c/clam/minima[0])
    ...       q1s_pred[j,i] = 0.610 + 0.062*epsilon + 0.027*epsilon**2 + 0.012*epsilon**3 + 0.004*epsilon**4
    ...    p= pl.subplot(131)
    ...    p= pl.plot(vsinis,q1s[j],'o',label='$\epsilon$=%.2f'%(epsilon));pl.gca()._get_lines.count -= 1
    ...    p= pl.plot(vsinis,q1s_pred[j],'-')
    ...    p= pl.subplot(133)
    ...    p= pl.plot(lambdas,G,'-')

    And plot the results:

    >>> p= pl.subplot(131)
    >>> p= pl.xlabel('v sini [km/s]');p = pl.ylabel('q1')
    >>> p= pl.legend(prop=dict(size='small'))


    >>> p= pl.subplot(132);p=pl.title('Fourier transform')
    >>> p= pl.plot(x,g**2,'k-',label='Analytical FT')
    >>> p= pl.plot(myx,g_/max(g_),'r-',label='Computed FT')
    >>> p= pl.plot(minima*2*pi*delta,minvals/max(g_),'bo',label='Minima')
    >>> p= pl.legend(prop=dict(size='small'))
    >>> p= pl.gca().set_yscale('log')

    ]include figure]]ivs_spectra_tools_vsini_kernel.png]

    Extra keyword arguments are passed to L{pergrams.deeming}

    @param wave: wavelength array in Angstrom
    @type wave: ndarray
    @param flux: normalised flux of the profile
    @type flux: ndarray
    @rtype: (array,array),(array,array),array,float
    @return: periodogram (freqs in s/km), extrema (weird units), vsini values (km/s), error (km/s)
    """
    cc = conversions.convert('m/s', 'AA/s', constants.cc)
    #-- clip the wavelength and flux region if needed:
    if window is not None:
        keep = (window[0] <= wave) & (wave <= window[1])
        wave, flux = wave[keep], flux[keep]
    #-- what is the central wavelength? If not given, it's the middle of the
    #   wavelength range
    if clam is None: clam = ((wave[0] + wave[-1]) / 2)
    #-- take care of the limb darkening
    q1 = 0.610 + 0.062 * epsilon + 0.027 * epsilon**2 + 0.012 * epsilon**3 + 0.004 * epsilon**4
    #-- do the Fourier transform and normalise
    #flux = flux / (np.median(np.sort(flux)[-5:]))
    freqs, ampls = pergrams.deeming(wave, (1 - flux), **kwargs)
    ampls = ampls / max(ampls)
    error = 1. / wave.ptp()
    #-- get all the peaks
    rise = np.diff(ampls[1:]) >= 0
    fall = np.diff(ampls[:-1]) <= 0
    minima = freqs[1:-1][rise & fall]
    minvals = ampls[1:-1][rise & fall]
    #-- compute the vsini and convert to km/s
    freqs = freqs * clam / q1 / cc
    freqs = conversions.convert('s/AA', 's/km', freqs, wave=(clam, 'AA'))
    vsini_values = cc / clam * q1 / minima
    vsini_values = conversions.convert('AA/s', 'km/s',
                                       vsini_values)  #,wave=(clam,'AA'))
    #-- determine the error as the rayleigh limit
    error = error * clam / q1 / cc
    error = conversions.convert('s/AA', 's/km', error, wave=(clam, 'AA'))
    return (freqs, ampls), (minima, minvals), vsini_values, error
Beispiel #44
0
def combine(list_of_spectra,
            R=200.,
            lambda0=(950., 'AA'),
            lambdan=(3350., 'AA')):
    """
    Combine and weight-average spectra on a common wavelength grid.

    C{list_of_spectra} should be a list of lists/arrays. Each element in the
    main list should be (wavelength,flux,error).

    If you have FUSE fits files, use L{ivs.fits.read_fuse}.
    If you have IUE FITS files, use L{ivs.fits.read_iue}.

    After Peter Woitke.

    @param R: resolution
    @type R: float
    @param lambda0: start wavelength, unit
    @type lambda0: tuple (float,str)
    @param lambdan: end wavelength, unit
    @type lambdan: tuple (float,str)
    @return: binned spectrum (wavelengths,flux, error)
    @rtype: array, array, array
    """
    l0 = conversions.convert(lambda0[1], 'AA', lambda0[0])
    ln = conversions.convert(lambdan[1], 'AA', lambdan[0])
    #-- STEP 1: define wavelength bins
    Delta = np.log10(1. + 1. / R)
    x = np.arange(np.log10(l0), np.log10(ln) + Delta, Delta)
    x = 10**x
    lamc_j = 0.5 * (np.roll(x, 1) + x)

    #-- STEP 2: rebinning of data onto newly defined wavelength bins
    Ns = len(list_of_spectra)
    Nw = len(lamc_j) - 1
    binned_fluxes = np.zeros((Ns, Nw))
    binned_errors = np.inf * np.ones((Ns, Nw))

    for snr, (wave, flux, err) in enumerate(list_of_spectra):
        wave0 = np.roll(wave, 1)
        wave1 = np.roll(wave, -1)
        lam_i0_dc = 0.5 * (wave0 + wave)
        lam_i1_dc = 0.5 * (wave1 + wave)
        dlam_i = lam_i1_dc - lam_i0_dc

        for j in range(Nw):
            A = np.min(np.vstack(
                [lamc_j[j + 1] * np.ones(len(wave)), lam_i1_dc]),
                       axis=0)
            B = np.max(np.vstack([lamc_j[j] * np.ones(len(wave)), lam_i0_dc]),
                       axis=0)
            overlaps = scipy.stats.threshold(A - B, threshmin=0)
            norm = np.sum(overlaps)
            binned_fluxes[snr, j] = np.sum(flux * overlaps) / norm
            binned_errors[snr, j] = np.sqrt(np.sum((err * overlaps)**2)) / norm

    #-- STEP 3: all available spectra sets are co-added, using the inverse
    #   square of the bin uncertainty as weight
    binned_fluxes[np.isnan(binned_fluxes)] = 0
    binned_errors[np.isnan(binned_errors)] = 1e300
    weights = 1. / binned_errors**2

    totalflux = np.sum(weights * binned_fluxes, axis=0) / np.sum(weights,
                                                                 axis=0)
    totalerr = np.sqrt(np.sum(
        (weights * binned_errors)**2, axis=0)) / np.sum(weights, axis=0)
    totalspec = np.sum(binned_fluxes > 0, axis=0)

    #-- that's it!
    return x[:-1], totalflux, totalerr, totalspec
def get_law(name,norm='E(B-V)',wave_units='AA',photbands=None,**kwargs):
    """
    Retrieve an interstellar reddening law.
    
    Parameter C{name} must be the function name of one of the laws defined in
    this module.
    
    By default, the law will be interpolated on a grid from 100 angstrom to
    10 micron in steps of 10 angstrom. This can be adjusted with the parameter
    C{wave} (array), which B{must} be in angstrom. You can change the units
    ouf the returned wavelength array via C{wave_units}.
    
    By default, the curve is normalised with respect to E(B-V) (you get
    A(l)/E(B-V)). You can set the C{norm} keyword to Av if you want A(l)/Av.
    Remember that
    
    A(V) = Rv * E(B-V)
    
    The parameter C{Rv} is by default 3.1, other reasonable values lie between
    2.0 and 5.1
    
    Extra accepted keywords depend on the type of reddening law used.
    
    Example usage:
    
    >>> wave = np.r_[1e3:1e5:10]
    >>> wave,mag = get_law('cardelli1989',wave=wave,Rv=3.1)
    
    @param name: name of the interstellar law
    @type name: str, one of the functions defined here
    @param norm: type of normalisation of the curve
    @type norm: str (one of E(B-V), Av)
    @param wave_units: wavelength units
    @type wave_units: str (interpretable for units.conversions.convert)
    @param photbands: list of photometric passbands
    @type photbands: list of strings
    @keyword wave: wavelength array to interpolate the law on
    @type wave: ndarray
    @return: wavelength, reddening magnitude
    @rtype: (ndarray,ndarray)
    """
    #-- get the inputs
    wave_ = kwargs.pop('wave',None)
    Rv = kwargs.setdefault('Rv',3.1)
    
    #-- get the curve
    wave,mag = globals()[name.lower()](**kwargs)
    wave_orig,mag_orig = wave.copy(),mag.copy()
    
    #-- interpolate on user defined grid
    if wave_ is not None:
        if wave_units != 'AA':
            wave_ = conversions.convert(wave_units,'AA',wave_)
        mag = np.interp(wave_,wave,mag,right=0)
        wave = wave_
           
    #-- pick right normalisation: convert to A(lambda)/Av if needed
    if norm.lower()=='e(b-v)':
        mag *= Rv
    else:
        #-- we allow ak and av as shortcuts for normalisation in JOHNSON K and
        #   V bands
        if norm.lower()=='ak':
            norm = 'JOHNSON.K'
        elif norm.lower()=='av':
            norm = 'JOHNSON.V'
        norm_reddening = model.synthetic_flux(wave_orig,mag_orig,[norm])[0]
        logger.info('Normalisation via %s: Av/%s = %.6g'%(norm,norm,1./norm_reddening))
        mag /= norm_reddening
    
    #-- maybe we want the curve in photometric filters
    if photbands is not None:
        mag = model.synthetic_flux(wave,mag,photbands)
        wave = filters.get_info(photbands)['eff_wave']
    
    
    #-- set the units of the wavelengths
    if wave_units != 'AA':
        wave = conversions.convert('AA',wave_units,wave)
    
    return wave,mag
def make_adipls_inputfile(model_name='model',degree=0,f0=None,fn=None,nf=1000,
                          fspacing='square',**kwargs):
    """
    Prepare an input file for ADIPLS.
    
    fspacing is uniform in 'square'd frequency, 'frequency' or 'period'.
    """
    global_params = dict(input_file=model_name+'.amdl',model_name=model_name)
    #-- parameter names
    par_names = {}
    #-- equilibirum model controls (mod)
    par_names['mod'] = [['ifind','xmod','imlds','in','nprmod'],
                        ['xtrnct','ntrnsf','imdmod']]
    #-- controls for mode selection
    par_names['osc'] = [['el','nsel','els1','dels','dfsig1','dfsig2','nsig1','nsig2'],
                        ['itrsig','sig1','istsig','inomde','itrds'],
                        ['dfsig','nsig','iscan','sig2'],
                        ['eltrw1','eltrw2','sgtrw1','sgtrw2']]
    #-- controls for rotational solution
    #par_names['rot'] = ['irotsl','em','nsem','ems1','dems']
    #-- controls for fundamental constants
    par_names['cst'] = [['cgrav']]
    #-- controls for equations and integration
    par_names['int'] = [['iplneq','iturpr','icow','alb'],
                        ['istsbc','fctsbc','ibotbc','fcttbc'],
                        ['mdintg','iriche','xfit','fcnorm','eps','epssol','itmax','dsigre'],
                        ['fsig','dsigmx','irsevn','xmnevn','nftmax','itsord']]
    #-- controls for output
    par_names['out'] = [['istdpr','nout','nprcen','irsord','iekinr'],
                        ['iper','ivarf','kvarf','npvarf','nfmode'],
                        ['irotkr','nprtkr','igm1kr','npgmkr','ispcpr'],
                        ['icaswn','sigwn1','sigwn2','frqwn1','frqwn2','iorwn1','iorwn2','frlwn1','frlwn2']]
    par_names['dgn'] = [['itssol','idgtss','moddet','iprdet','npout'],
                        ['imstsl','imissl','imjssl','idgnrk']]
    
    #-- set degree of the mode
    if not hasattr(degree,'__iter__'):
        kwargs['el'] = degree
        kwargs['nsel'] = 0
    #-- set frequency range
    #   if f0 and fn are not given as a tuple, they are dimensionless
    if f0 is not None or fn is not None:
        kwargs['istsig'] = 2 # sig1 and sig2 are limits in cyclic frequency nu
        if f0 is not None: kwargs['sig1'] = conversions.convert(f0[1],'mHz',f0[0])
        if fn is not None: kwargs['sig2'] = conversions.convert(fn[1],'mHz',fn[0])
    if f0 is not None and fn is not None:
        #-- frequency range
         kwargs['itrsig'] = 1 # check for change of sign in the matching
                         # determinant and iterate for eigenfrequency
                         # at each change of sign
         #-- set density of trial frequency range
         kwargs['iscan'] = nf
    elif f0 is not None and fn is None:
        #-- explicit setting of frequency, e.g. when computing eigenfrequencies
        kwargs['itrsig'] = 0 # trial frequency taken from sig1
        raise ValueError,'istsig=2 not allowed with itrsig=0'
    
    #-- set frequency step
    if 's' in fspacing.lower():
        kwargs['nsig'] = 1 # square in frequency for low order p modes
    elif 'p' in fspacing.lower():
        kwargs['nsig'] = 2 # linear in frequency for p modes
    elif 'g' in fspacing.lower():
        kwargs['nsig'] = 3 # linear in period for g modes
    else:
        raise ValueError,"Cannot interpret fspacing value"
    
    #-- check for nans
    for sec in par_names.keys():
        for line in par_names[sec]:
            for par in line:
                if np.isnan(kwargs.get(par,0)):
                    raise ValueError,'%s:%s is nan'%(sec,par)
    #-- write the parameter control input file
    control_file = model_name+'.in'
    #os.system('cp tempmodel.amdlr %s'%(model_name+'.amdl')) # historical debugging
    #a,b = special.read_amdl('tempmodel.amdlr') # historical debugging
    #special.write_amdl(a,b,model_name+'.amdl') # historical debugging
    
    with open(control_file,'w') as ff:
        #-- first write global parameters
        ff.write("2 '{0}'    @\n".format(model_name+'.amdl'))
        #ff.write("2 '{0}'    @\n".format('tempmodel.amdlr')) # historical debugging
        ff.write("9 '0'    @\n")
        ff.write("11 '{0}'    @\n".format(model_name+'.gsm'))
        ff.write("15 '{0}'    @\n".format(model_name+'.ssm'))
        if 'nfmode' in kwargs and kwargs['nfmode']>0:
            ff.write("4 '{0}'    @\n".format(model_name+'.aeig'))
            
        ff.write("-1 ''    @\n")
        #-- then specific parameters
        #   the sections have to be given in a specific order
        sections = [sec for sec in ['mod','osc','cst','int','out','dgn'] if sec in par_names.keys()]
        
        ff.write('\n')
        ff.write('cntrd,\n%s     @\n'%('.'.join(sections)))
        for section in sections:
            ff.write('%s:\n'%(section))
            #-- write line per line:
            for line in par_names[section]:
                #   first the names of the parameters for clarity
                ff.write(','.join(line)+'\n')
                #   then the values
                values = [str(kwargs.get(par_name,'')) for par_name in line]
                ff.write(','.join(values)+',     @\n')
    #-- that's it!  
    return control_file