1  """ 
  2  Derive local quantities such as effective temperature and surface gravity for Roche potentials. 
  3  """ 
  4  import numpy as np 
  5  import pylab as pl 
  6  from numpy import pi,cos,sin,sqrt 
  7  from scipy.optimize import newton 
  8  try: 
  9      from scipy.spatial import Delaunay 
 10  except ImportError: 
 11      print 'No Delaunay' 
 12   
 13  from ivs.sed import limbdark 
 14  from ivs.coordinates import vectors 
 15   
 16   
 17   
 18   
 20      """ 
 21      Construct a coordinate grid 
 22       
 23      If you give two resolutions, the first is for theta, the second for phi 
 24       
 25      @param args: one or two integers indicating number of grid points in theta 
 26      and phi direction 
 27      @type args: integer 
 28      @keyword gtype: grid type ('spher' or 'delaunay') 
 29      @type gtype: str 
 30      @return: theta,phi(,grid) 
 31      """ 
 32      gtype = kwargs.get('gtype','spherical') 
 33      full = kwargs.get('full',False) 
 34       
 35      if 'spher' in gtype.lower(): 
 36          if len(args)==1: res1 = res2 = args[0]  
 37          else:            res1,res2 = args       
 38           
 39          dtheta = pi/res1  
 40          dphi = pi/res2      
 41           
 42           
 43          if full: 
 44              theta0,thetan = dtheta, pi-dtheta 
 45              phi0,phin = 0,2*pi-2*dphi 
 46          else: 
 47              theta0,thetan = dtheta/4,pi/2-dtheta/4 
 48              phi0,phin = 0,pi-dphi 
 49               
 50          theta,phi = np.mgrid[theta0:thetan:res1*1j,phi0:phin:res2*1j] 
 51          return theta,phi 
 52       
 53      elif 'cil' in gtype.lower(): 
 54          if len(args)==1: res1 = res2 = args[0]  
 55          else:            res1,res2 = args       
 56           
 57          dsintheta = 2./res1  
 58          dphi = pi/res2      
 59           
 60           
 61          if full: 
 62              sintheta0,sinthetan = -1+dsintheta, 1.-dsintheta 
 63              phi0,phin = 0,2*pi-dphi 
 64          else: 
 65              sintheta0,sinthetan = dsintheta,1-dsintheta 
 66              phi0,phin = 0,pi-dphi 
 67               
 68          sintheta,phi = np.mgrid[sintheta0:sinthetan:res1*1j,phi0:phin:res2*1j] 
 69          return np.arccos(sintheta),phi 
 70       
 71      elif 'delaunay' in gtype.lower(): 
 72           
 73          if len(args)==1: res1 = res2 = args[0]  
 74          else:            res1,res2 = args       
 75           
 76          u,v = np.random.uniform(size=res1*res2),np.random.uniform(size=res1*res2) 
 77          phi = 2*pi*u 
 78          theta = np.arccos(2*v-1) 
 79   
 80          x = sin(theta)*sin(phi) 
 81          y = sin(theta)*cos(phi) 
 82          z = cos(theta) 
 83           
 84          points = np.array([x,y,z]).T 
 85          grid = Delaunay(points) 
 86          centers = np.zeros((len(grid.convex_hull),3)) 
 87          for i,indices in enumerate(grid.convex_hull): 
 88              centers[i] = [x[indices].sum()/3,y[indices].sum()/3,z[indices].sum()/3] 
 89          theta,phi = np.arccos(centers[:,2]),np.arctan2(centers[:,1],centers[:,0])+pi 
 90           
 91          return theta,phi,grid 
 92           
 93      elif 'tri' in gtype.lower(): 
 94           
 95          if len(args)==1: res1 = res2 = args[0]  
 96          else:            res1,res2 = args       
 97           
 98          u,v = np.random.uniform(size=res1*res2),np.random.uniform(size=res1*res2) 
 99          phi = 2*pi*u 
100          theta = np.arccos(2*v-1) 
101          return theta,phi 
 102           
103       
104           
105           
106           
107       
108   
110      """ 
111      Stitch a grid together that was originally defined on 1 quadrant. 
112       
113      We add the three other quandrants. 
114      """ 
115      seamless = kwargs.get('seamless',False) 
116      vtype = kwargs.get('vtype',['scalar' for i in quant]) 
117      gtype = kwargs.get('gtype','spher') 
118      ravel = kwargs.get('ravel',False) 
119       
120      if gtype == 'spher': 
121           
122          alltheta = np.vstack([np.hstack([theta,theta]),np.hstack([theta+pi/2,theta+pi/2])]) 
123          allphi   = np.vstack([np.hstack([phi,phi+pi]),np.hstack([phi,phi+pi])]) 
124       
125           
126          allquan = [] 
127          for i,iquant in enumerate(quant): 
128               
129              top1,top2 = iquant,iquant[:,::-1] 
130              bot1,bot2 = iquant[::-1],iquant[::-1][:,::-1] 
131              if vtype[i]=='scalar': 
132                  allquan.append(np.vstack([np.hstack([top1,top2]),np.hstack([bot1,bot2])])) 
133               
134              elif vtype[i]=='x': 
135                  allquan.append(np.vstack([np.hstack([top1,top2]),np.hstack([bot1,bot2])])) 
136              elif vtype[i]=='y': 
137                  allquan.append(np.vstack([np.hstack([top1,-top2]),np.hstack([bot1,-bot2])])) 
138              elif vtype[i]=='z': 
139                  allquan.append(np.vstack([np.hstack([top1,top2]),np.hstack([-bot1,-bot2])])) 
140              elif vtype[i]=='vx': 
141                  allquan.append(np.vstack([np.hstack([top1,-top2]),np.hstack([bot1,-bot2])])) 
142              elif vtype[i]=='vy': 
143                  allquan.append(np.vstack([np.hstack([top1,top2]),np.hstack([bot1,bot2])])) 
144              elif vtype[i]=='vz': 
145                  allquan.append(np.vstack([np.hstack([top1,top2]),np.hstack([-bot1,-bot2])])) 
146           
147          out = [alltheta,allphi]+allquan 
148           
149           
150          if seamless: 
151               
152              out = [np.column_stack([i,i[:,0]]) for i in out] 
153              out[1][:,-1] += 2*pi 
154               
155              out = [np.vstack([i,i[0]]) for i in out] 
156              out[0][-1] += pi 
157           
158           
159          if ravel: 
160              out = [i.ravel() for i in out] 
161      else: 
162          out = [theta,phi] + list(quant) 
163           
164      return out 
 165   
166   
168      """ 
169      Numerically compute surface normals of a grid (in absence of analytical alternative). 
170       
171      Also computes the surface elements, making L{surface_elements} obsolete. 
172      """ 
173      if gtype=='spher': 
174          raise NotImplementedError 
175      elif gtype=='delaunay': 
176          raise NotImplementedError 
177      elif gtype=='triangular': 
178           
179          x,y,z = vectors.spher2cart_coord(r,phi,theta) 
180           
181          centers = np.zeros((len(grid.convex_hull),3)) 
182          normals = np.zeros((len(grid.convex_hull),3)) 
183          sizes = np.zeros(len(grid.convex_hull)) 
184           
185           
186   
187           
188          for i,indices in enumerate(grid.convex_hull): 
189               
190              centers[i] = [x[indices].sum()/3,y[indices].sum()/3,z[indices].sum()/3] 
191               
192              a = sqrt((x[indices[0]]-x[indices[1]])**2 + (y[indices[0]]-y[indices[1]])**2 + (z[indices[0]]-z[indices[1]])**2) 
193              b = sqrt((x[indices[0]]-x[indices[2]])**2 + (y[indices[0]]-y[indices[2]])**2 + (z[indices[0]]-z[indices[2]])**2) 
194              c = sqrt((x[indices[1]]-x[indices[2]])**2 + (y[indices[1]]-y[indices[2]])**2 + (z[indices[1]]-z[indices[2]])**2) 
195              s = 0.5*(a+b+c) 
196              sizes[i] = sqrt( s*(s-a)*(s-b)*(s-c)) 
197               
198              side1 = [x[indices[1]]-x[indices[0]],y[indices[1]]-y[indices[0]],z[indices[1]]-z[indices[0]]] 
199              side2 = [x[indices[2]]-x[indices[0]],y[indices[2]]-y[indices[0]],z[indices[2]]-z[indices[0]]] 
200              normals[i] = np.cross(side1,side2) 
201           
202           
203          normal_r,normal_phi,normal_theta = vectors.cart2spher(centers.T,normals.T) 
204          normal_r = np.abs(normal_r) 
205          centers_sph = vectors.cart2spher_coord(*centers.T) 
206          normals = np.array(vectors.spher2cart(centers_sph,(normal_r,normal_phi,normal_theta))) 
207           
208           
209          normals_T = normals.T 
210          normals = normals_T / vectors.norm(normals_T) 
211           
212          print centers.shape,sizes.shape,normals.shape 
213          return centers, sizes, normals 
 214   
215   
216   
217 -def surface_elements((r,mygrid),(surfnormal_x,surfnormal_y,surfnormal_z),gtype='spher'): 
 218      """ 
219      Compute surface area of elements in a grid. 
220       
221      theta,phi must be generated like mgrid(theta_range,phi_range) 
222       
223      usually, the surfnormals are acquired via differentiation of a gravity potential, 
224      and is then equal to the *negative* of the local surface gravity. 
225      """ 
226      theta,phi = mygrid[:2] 
227      if gtype=='spher': 
228           
229          dtheta = theta[1:]-theta[:-1] 
230          dtheta = np.vstack([dtheta,dtheta[-1]]) 
231           
232          dphi = phi[:,1:]-phi[:,:-1] 
233          dphi = np.column_stack([dphi,dphi[:,-1]]) 
234           
235          x,y,z = vectors.spher2cart_coord(r,phi,theta) 
236           
237          a = np.array([x,y,z]) 
238          b = np.array([surfnormal_x,surfnormal_y,surfnormal_z]) 
239           
240          cos_gamma = vectors.cos_angle(a,b) 
241           
242          return r**2 * sin(theta) * dtheta * dphi / cos_gamma, cos_gamma 
243       
244      elif gtype=='delaunay': 
245           
246          x,y,z = vectors.spher2cart_coord(r,phi,theta) 
247          a = np.array([x,y,z]) 
248          b = np.array([surfnormal_x,surfnormal_y,surfnormal_z])         
249          cos_gamma = vectors.cos_angle(a,b) 
250           
251          delaunay_grid = mygrid[2] 
252   
253          sizes = np.zeros(len(delaunay_grid.convex_hull)) 
254          points = delaunay_grid.points 
255          vertx,verty,vertz = points.T 
256           
257           
258           
259           
260           
261           
262          centers = np.zeros((len(delaunay_grid.convex_hull),3)) 
263          for i,indices in enumerate(delaunay_grid.convex_hull): 
264               
265              a = sqrt((vertx[indices[0]]-vertx[indices[1]])**2 + (verty[indices[0]]-verty[indices[1]])**2 + (vertz[indices[0]]-vertz[indices[1]])**2) 
266              b = sqrt((vertx[indices[0]]-vertx[indices[2]])**2 + (verty[indices[0]]-verty[indices[2]])**2 + (vertz[indices[0]]-vertz[indices[2]])**2) 
267              c = sqrt((vertx[indices[1]]-vertx[indices[2]])**2 + (verty[indices[1]]-verty[indices[2]])**2 + (vertz[indices[1]]-vertz[indices[2]])**2) 
268              s = 0.5*(a+b+c) 
269              sizes[i] = sqrt( s*(s-a)*(s-b)*(s-c)) 
270               
271           
272           
273           
274           
275           
276           
277          return sizes*r**2, cos_gamma 
 278   
279 -def temperature(surface_gravity,g_pole,T_pole,beta=1.): 
 280      """ 
281      Calculate local temperature. 
282       
283      beta is gravity darkening parameter. 
284      """ 
285      Grav = abs(surface_gravity/g_pole)**beta 
286      Teff = Grav**0.25 * T_pole 
287      return Teff 
 288   
289 -def intensity(teff,grav,mu=None,photband='OPEN.BOL'): 
 290      """ 
291      Calculate local intensity. 
292       
293      beta is gravity darkening parameter. 
294      """ 
295      if mu is None: 
296          mu = np.ones_like(teff) 
297      if (teff<3500).any() or np.isnan(teff).any(): 
298          print 'WARNING: point outside of grid, minimum temperature is 3500K' 
299          teff = np.where((teff<3500) | np.isnan(teff),3500,teff) 
300      if (grav<0.01).any() or np.isnan(grav).any(): 
301          print 'WARNING: point outside of grid, minimum gravity is 0 dex' 
302          grav = np.where((np.log10(grav*100)<0.) | np.isnan(grav),0.01,grav) 
303      intens = np.array([limbdark.get_itable(teff=iteff,logg=np.log10(igrav*100),absolute=True,mu=imu,photbands=[photband])[0] for iteff,igrav,imu in zip(teff.ravel(),grav.ravel(),mu.ravel())]) 
304      return intens.reshape(teff.shape) 
 305       
306   
308      """ 
309      Compute projected intensity in the line of sight. 
310       
311      gravity is vector directed inwards in the star 
312      line of sight is vector. 
313      """ 
314      ones = np.ones_like(gravity[0]) 
315      losx = line_of_sight[0]*ones 
316      losy = line_of_sight[1]*ones 
317      losz = line_of_sight[2]*ones 
318      angles = vectors.angle(-gravity,np.array([losx,losy,losz])) 
319      mus = cos(angles) 
320      grav_ = vectors.norm(gravity) 
321       
322      intens = intensity(teff,grav_,mu=mus,photband=photband) 
323       
324       
325      return intens*areas*mus,mus 
 326   
327   
328 -def project(star,view_long=(0,0,0),view_lat=(pi/2,0,0),photband='OPEN.BOL', 
329              only_visible=False,plot_sort=False,scale_factor=1.): 
 330      """ 
331      Project and transform coordinates and vectors to align with the line-of-sight. 
332       
333      Parameter C{star} should be a record array containing fields 'teff','gravx', 
334      'gravy','gravz','areas','vx','vy','vz' 
335       
336      and either you suply ('r','theta','phi') or ('x','y','z') 
337       
338      The XY direction is then the line-of-sight, and the YZ plane is the plane 
339      of the sky. 
340       
341      An extra column 'projflux' and 'eyeflux' will be added. Projected flux 
342      takes care of limb darkening, and projected surface area. Eye flux only 
343      takes care of limbdarkening, and should only be used for plotting reasons. 
344       
345      view_long[0] of 0 means looking in the XY line, pi means looking in the YX line. 
346      view_lat[0] of pi/2 means edge on, 0 or pi is pole-on. 
347       
348      This function updates all Cartesian coordinates present in the star, but not 
349      the polar coordinates! The projected fluxes are added as a field 'projflux' 
350      to the returned record array. 
351       
352      If you set 'only_visible' to True, only the information on the visible parts 
353      of the star will be contained. 
354       
355      If you set 'plot_sort' to True, the arrays will be returned in a sorted order, 
356      where the areas at the back come first. This is especially handy for plotting. 
357       
358      @parameters star: record array containing all necessary information on the 
359      star 
360      @type star: numpy record array 
361      @parameter view_long: longitude viewing angle (radians) and coordinate zeropoint 
362      @type view_long: tuple floats  (radians,x,y) 
363      @parameter view_lat: inclination viewing angle (radians) and coordinate zeropoint 
364      @type view_lat: tuple floats (radians,x,z) 
365      @parameter photband: photometric passband 
366      @type photband: string 
367      @parameter only_visible: flag to return only information on visible surface elements 
368      @type only_visible: boolean 
369      @parameter plot_sort: flag to sort the surface elements from back to front 
370      @type plot_sort: boolean 
371      """ 
372      myshape = star['gravx'].shape 
373      gravx,gravy,gravz = np.array([star['gravx'].ravel(),star['gravy'].ravel(),star['gravz'].ravel()]) 
374      areas = star['areas'].ravel() 
375      teff = star['teff'].ravel() 
376      vx,vy,vz = star['vx'].ravel(),star['vy'].ravel(),star['vz'].ravel() 
377       
378       
379      if not 'x' in star.dtype.names: 
380          x,y,z = vectors.spher2cart_coord(star['r'].ravel(),star['phi'].ravel(),star['theta'].ravel()) 
381      else: 
382          x,y,z = star['x'].ravel(),star['y'].ravel(),star['z'].ravel(), 
383       
384       
385       
386      x,y = vectors.rotate(x,y,view_long[0],x0=view_long[1],y0=view_long[2]) 
387      gravx,gravy = vectors.rotate(gravx,gravy,view_long[0]) 
388      vx,vy = vectors.rotate(vx,vy,view_long[0]) 
389       
390      if view_lat[0]!=pi/2: 
391          rot_i = -(pi/2 - view_lat[0]) 
392          x,z = vectors.rotate(x,z,rot_i) 
393          gravx,gravz = vectors.rotate(gravx,gravz,rot_i) 
394          vx,vz = vectors.rotate(vx,vz,rot_i) 
395       
396       
397      view_vector = np.array([1.,0,0]) 
398      grav_local = np.array([gravx,gravy,gravz]) 
399      proj_flux,mus = projected_intensity(teff,grav_local,areas,view_vector,photband=photband) 
400       
401       
402       
403      new_star = star.copy() 
404      new_star['gravx'],new_star['gravy'],new_star['gravz'] = gravx,gravy,gravz 
405      new_star['vx'],new_star['vy'],new_star['vz'] = vx,vy,vz 
406      if 'x' in star.dtype.names: 
407          new_star['x'],new_star['y'],new_star['z'] = x*scale_factor,y*scale_factor,z*scale_factor 
408      else: 
409          new_star = pl.mlab.rec_append_fields(new_star,'x',x*scale_factor) 
410          new_star = pl.mlab.rec_append_fields(new_star,'y',y*scale_factor) 
411          new_star = pl.mlab.rec_append_fields(new_star,'z',z*scale_factor) 
412      new_star = pl.mlab.rec_append_fields(new_star,'projflux',proj_flux) 
413      new_star = pl.mlab.rec_append_fields(new_star,'eyeflux',proj_flux/areas) 
414      new_star = pl.mlab.rec_append_fields(new_star,'mu',mus) 
415       
416       
417      if only_visible: 
418          new_star = new_star[-np.isnan(new_star['projflux'])] 
419      if plot_sort: 
420          new_star = new_star[np.argsort(new_star['x'])] 
421      return new_star 
 422