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
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 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
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
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 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 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))
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
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
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
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
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
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 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 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
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
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
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
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
#=================================================================================== # 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])
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 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