1
2
3 """
4 Module for calculating the velocity profile as a function of radius.
5
6 Author: R. Lombaert
7
8 """
9
10 import numpy as np
11 from astropy import constants as cst
12 from astropy import units as u
13 from scipy.integrate import trapz
14
15 from cc.modeling.profilers import Profiler
16 from cc.modeling.profilers import Mdot
17
18
19
20 -def driftRPDF(r,a,l,v,mdot,opac,sd,radiance,T=None,P=0,alpha=0.,mu=2.,\
21 w_thermal='none'):
22
23 '''
24 Calculate the drift velocity as a function of radius and grain size by
25 equating the radiation pressure force and drag force.
26
27 If the grain size is given as a constant value, the profile is calculated
28 only for an average grain size, and is essentially 1d, in which case the
29 drift does not depend on grain size.
30
31 For now in the approximation that the thermal velocities are small compared
32 to the drift. This is only valid in the outer, cool regions of the wind.
33
34 The function integrates the luminosity and the opacity over the wavelength
35 grid to estimate the component for the radiation pressure. Note that the
36 opacities are assumed to be independent of grain size for this to work,
37 hence the Rayleigh regime should be applicable.
38
39 Inclusion of the thermal velocity requires T profile. Mean molecular weight
40 assumed to be one in that case.
41
42 @param r: The radial points (cm)
43 @type r: array/float
44 @param a: The grain size grid (cm)
45 @type a: array/float
46 @param l: The wavelength grid (cm)
47 @type l: array/float
48 @param v: The velocity profile (cm/s)
49 @type v: float/Velocity()
50 @param mdot: The mass-loss profile (Msun/yr)
51 @type mdot: float/Mdot()
52 @param opac: The opacity profile (cm2/g), must include l-dependence. Can be
53 extinction, absorption, or scattering, but note that the
54 calculation assumes this is an absorption opacity.
55 @type opac: Opacity()
56 @param sd: The specific density of the dust species
57 @type sd: float
58 @param radiance: The luminosity profile (ergs/s), must include l-dependence
59 @type radiance: Radiance()
60
61 @keyword T: The temperature profile. Only relevant if w_thermal is not
62 none.
63
64 (default: None)
65 @type T: Temperature()
66 @keyword P: The porosity of the grain (or equivalent, to represent
67 effective grain surface). Default is the spherical case. Is
68 given as the ratio of vacuum per unit volume of a grain.
69
70 (default: 0)
71 @type P: float
72 @keyword alpha: Sticking coefficient (Kwok 1975, Van Marle 2011). Default is
73 current value in MCP/GASTRoNOoM. Should be 0.25 according to
74 Kwok and van Marle. 1-alpha gives the fraction of elastic
75 collisions.
76
77 (default: 0.)
78 @type alpha: float
79 @keyword mu: The mean molecular weight. Default is a wind with purely H2.
80 To get the GASTRoNOoM value of 1.4, set fH to 1.5.
81
82 (default: 2.0)
83 @type mu: float
84 @keyword w_thermal: Type of thermal velocity.
85 - sqrt(2kT/m): most probably speed df(v)/dr = 0
86 - sqrt(8/pi kT/m): mean speed (f(v) * v dv)/f(v) dv
87 - sqrt(3kT/m): rms speed sqrt((f(v) * v2 dv)/f(v) dv)
88 - Kwok version: 3/4 sqrt(3kT/m)
89 - Epstein: 4/3 sqrt(8kT/mpi)
90 Choose from: epstein, kwok, prob, mean, rms, none
91
92 (default: none)
93 @type w_thermal: str
94
95 @return: The drift velocity (cm/s) as a function of r and a
96 @rtype: array/float
97
98 '''
99
100
101 c = cst.c.cgs.value
102 m_p = cst.m_p.cgs.value
103 k_b = cst.k_B.cgs.value
104 w_thermal = w_thermal.lower()
105
106
107 vi = v.eval(r) if isinstance(v,Velocity) else v
108 mdoti = mdot.eval(r) if isinstance(mdot,Mdot.Mdot) else mdot
109 mdoti = (mdoti*u.Msun/u.yr).to(u.g/u.s).value
110
111
112
113 L = radiance.getLuminosity(l=l,ftype='flambda')
114
115
116
117 OpacL = trapz(x=l,y=opac.eval(l)*L)
118
119
120
121
122
123 arr = np.outer(vi/mdoti,a)
124 vK = np.sqrt(arr/(c*(1-alpha))*OpacL*4./3.*sd*(1.-P)**(2./3.))
125
126
127 if not w_thermal in ['kwok','mean','rms','prob','epstein']:
128 return vK
129
130
131 if w_thermal == 'rms':
132 vT = (3.*k_b*T.eval(r)/(mu*m_p))**0.5
133
134
135 elif w_thermal == 'prob':
136 vT = (2.*k_b*T.eval(r)/(mu*m_p))**0.5
137
138
139 elif w_thermal == 'mean':
140 vT = (8.*k_b*T.eval(r)/(mu*m_p*np.pi))**0.5
141
142
143 elif w_thermal == 'epstein':
144 vT = 4/3.*(8.*k_b*T.eval(r)/(mu*m_p*np.pi))**0.5
145
146
147 else:
148 vT = 0.75*(3.*k_b*T.eval(r)/(mu*m_p))**0.5
149
150
151 vT = np.transpose([vT])
152 xT = 0.5*(vT/vK)**2.
153 factor = ((1.+xT**2)**0.5-xT)**0.5
154
155 return factor*vK
156
157
158
160
161 '''
162 The sound velocity at T for a given mean molecular weight and gamma.
163
164 @param T: The temperature (K)
165 @type T: array/float
166 @param gamma: The adiabatic constant
167 @type gamma: float
168 @param mu: the mean molecular weight in units of hydrogen mass (g)
169 @type mu: float
170
171 @return: The sound velocity
172 @rtype: array/float
173
174 '''
175
176
177 k_B = 1.3806488e-16
178 m_p = 1.672621777e-24
179
180 return np.sqrt(gamma*k_B*T/mu/m_p)
181
182
183
185
186 '''
187 Convenience function for vbeta in 2D. Used by Drift() to calculate a beta
188 law for a single grain size.
189
190 Ignores the grain size array, and passes arguments on to vbeta.
191
192 Additional args and kwargs are passed on to vbeta.
193
194 @param r: The radial points (cm)
195 @type r: array
196 @param a: Grain size (cm), dummy variable that is ignored.
197 @type a: array
198
199 @return: The beta law velocity profile (cm/s)
200 @rtype: array
201
202 '''
203
204 return vbeta(r,*args,**kwargs)
205
206
207
208 -def vbeta(r,r0,v0,vinf,beta,vi_mode='constant',rstar=0.0,beta_inner=0.5,\
209 *args,**kwargs):
210
211 '''
212 The beta-power law description of the velocity profile.
213
214 Below the cut-off r0, the velocity is taken constant and equal to v0. This
215 behaviour can be changed with the vi_mode keyword.
216
217 @param r: The radial points (cm)
218 @type r: array
219 @param r0: The inner radius (typically at dust condensation radius, cm)
220 @type r0: float
221 @param v0: The initial velocity at r0 (cm/s). Can be 'vs' for the sound
222 velocity, in which case additional args and kwargs must be passed
223 to the function call (see method vsound()).
224 @type v0: float
225 @param vinf: The terminal wind velocity (cm/s)
226 @type vinf: float
227 @param beta: The exponent of the beta law
228 @type beta: float
229
230 @keyword vi_mode: The type of velocity law used in the inner wind at r<r0.
231 Options:
232 - constant: A constant value at v0
233 - beta: an increasing velocity up to v0 according to a
234 beta law with beta=beta_inner, r0~rstar.
235 Lower case letters required.
236
237 (default: 'constant')
238 @type vi_mode: str
239 @keyword rstar: The stellar radius (cm), only relevant when
240 vi_mode==beta
241
242 (default: 0)
243 @type rstar: float
244 @keyword beta_inner: Only relevant for vi_mode==beta, the beta exponent for
245 the inner wind at r<r0. Default is value for GASTRonOoM
246
247 (default: 0.5)
248 @type beta_inner: float
249
250 @return: The beta law velocity profile (cm/s)
251 @rtype: array
252
253 '''
254
255
256 if v0 == 'vs':
257 v0 = vsound(*args,**kwargs)
258
259
260 v = v0 + (vinf-v0)*(1-r0/r)**beta
261
262
263 if vi_mode == 'constant':
264 vi = v0
265
266
267
268
269
270
271
272 elif vi_mode == 'beta':
273
274
275
276 rstar_scaled = rstar*(1 - 0.01**(1./beta_inner))
277
278
279
280
281
282
283
284 v0_factor = (1.-rstar_scaled/r0)**(-beta_inner)
285 vi = v0*v0_factor*(1-rstar_scaled/r)**beta_inner
286
287
288 return np.where(r<=r0,vi,v)
289
290
291
292 -def dvbetadr(r,r0,v0,vinf,beta,vi_mode='constant',rstar=0.0,beta_inner=0.5,\
293 *args,**kwargs):
294
295 '''
296 The derivative of the vbeta function: analytic.
297
298 Below the cut-off r0, the velocity is taken constant and equal to v0. This
299 behaviour can be changed with the vi_mode keyword. (NYI)
300
301 @param r: The radial points (cm)
302 @type r: array
303 @param r0: The inner radius (typically at dust condensation radius, cm)
304 @type r0: float
305 @param v0: The initial velocity at r0 (cm/s). Can be 'vs' for the sound
306 velocity, in which case additional args and kwargs must be passed
307 to the function call (see method vsound()).
308 @type v0: float
309 @param vinf: The terminal wind velocity (cm/s)
310 @type vinf: float
311 @param beta: The exponent of the beta law
312 @type beta: float
313
314 @keyword vi_mode: The type of velocity law used in the inner wind at r<r0.
315 Options:
316 - constant: A constant value at v0
317 - beta: an increasing velocity up to v0 according to a
318 beta law with beta=beta_inner, r0~rstar.
319 Lower case letters required.
320
321 (default: 'constant')
322 @type vi_mode: str
323 @keyword rstar: The stellar radius (cm), only relevant when
324 vi_mode==beta
325
326 (default: 0)
327 @type rstar: float
328 @keyword beta_inner: Only relevant for vi_mode==beta, the beta exponent for
329 the inner wind at r<r0. Default is value for GASTRonOoM
330
331 (default: 0.5)
332 @type beta_inner: float
333
334 @return: The analytic derivative of the beta law velocity profile (cm^2/s)
335 @rtype: array
336
337 '''
338
339
340 if v0 == 'vs':
341 v0 = vsound(*args,**kwargs)
342
343
344 dvdr = beta*(vinf-v0)*(1.-r0/r)**(beta-1)*r0/(r*r)
345
346
347 if vi_mode == 'constant':
348 dvi = 0.0
349
350
351
352 elif vi_mode == 'beta':
353
354
355
356 rstar_scaled = rstar*(1 - 0.01**(1./beta_inner))
357
358
359
360
361
362
363
364
365 v0_factor = (1.-rstar_scaled/r0)**(-beta_inner)
366 deriv_factor = rstar_scaled/(r*r)
367 dvi = v0*v0_factor*0.5*(1-rstar_scaled/r)**(beta_inner-1.)*deriv_factor
368
369
370
371 return np.where(r<=r0,dvi,dvdr)
372
373
374
375 -def vdust(r,v,w,*args,**kwargs):
376
377 '''
378 Calculate the dust velocity profile from a gas velocity and a drift velocity
379 profile.
380
381 Requires averaging the drift velocity over grain size. Several normalisation
382 possibilities available. See Drift().avgDrift.
383
384 @param r: The radial points (cm)
385 @type r: array
386 @param v: The velocity profile (cm/s)
387 @type v: float/Velocity()
388 @param w: The drift velocity profile (cm/s) as a function of r and a.
389 @type w: float/Drift()
390
391 @keyword args: Additional parameters passed to the avgDrift method
392
393 (default: [])
394 @type args: tuple
395 @keyword kwargs: Additional keywords passed to the avgDrift method
396
397 (default: {})
398 @type kwargs: dict
399
400 @return: The dust velocity profile (cm/s)
401 @rtype: array
402
403 '''
404
405 vi = v.eval(r) if isinstance(v,Velocity) else v
406 wi = w.avgDrift(r,*args,**kwargs) if isinstance(w,Drift) else w
407
408 return vi + wi
409
410
411
413
414 '''
415 An interface for a velocity profile and its derivative
416
417 '''
418
419 - def __init__(self,r,func,dfunc=None,order=3,*args,**kwargs):
420
421 '''
422 Create an instance of the Velocity() class. Requires a radial grid and
423 a velocity function.
424
425 The function can also be given as an interp1d object.
426
427 The optional args and kwargs give the additional arguments for the
428 velocity function, which are ignored in case func is an interp1d object.
429
430 @param r: The radial points (cm)
431 @type r: array
432 @param func: The function that describes the velocity profile. Can be
433 given as an interp1d object.
434 @type func: function
435
436 @keyword dfunc: The function that describes the derivative of the
437 profile with respect to r. Can be given as an interp1d
438 object. If None, a generic central difference is taken
439 and interpolated.
440
441 (default: None)
442 @type dfunc: function/interp1d object
443 @keyword order: Order of the spline interpolation. Default is cubic.
444
445 (default: 3)
446 @type order: int
447
448 @keyword args: Additional parameters passed to the functions when eval
449 or diff are called.
450
451 (default: [])
452 @type args: tuple
453 @keyword kwargs: Additional keywords passed to the functions when eval
454 or diff are called.
455
456 (default: {})
457 @type kwargs: dict
458 '''
459
460
461 if func == vbeta or func == 'vbeta':
462 dfunc = dvbetadr
463
464
465 super(Velocity, self).__init__(r,func,dfunc,order,*args,**kwargs)
466
467 self.r = self.x
468 self.v = self.y
469 self.dvdr = self.dydx
470
471
472
473 -class Drift(Profiler.Profiler2D):
474
475 '''
476 An interface for the drift velocity as a function of radius and grain size.
477
478 '''
479
480 - def __init__(self,r,a,func,*args,**kwargs):
481
482 '''
483 Create an instance of the Drift() class. Requires a radial grid, a grain
484 size grid and a velocity function.
485
486 The function can also be given as an interpolator object.
487
488 The optional args and kwargs give the additional arguments for the
489 drift function, which are ignored in case func is an interpolator object
490
491 @param r: The radial points (cm)
492 @type r: array
493 @param a: The grain size grid (cm)
494 @type a: array
495 @param func: The function that describes the drift profile. Can be
496 given as an interpolator object.
497 @type func: function
498
499 @keyword args: Additional parameters passed to the functions when eval
500 or diff are called.
501
502 (default: [])
503 @type args: tuple
504 @keyword kwargs: Additional keywords passed to the functions when eval
505 or diff are called.
506
507 (default: {})
508 @type kwargs: dict
509
510 '''
511
512 super(Drift, self).__init__(r,a,func,*args,**kwargs)
513 self.r = self.x
514 self.a = self.y
515 self.w = self.z
516
517
518
519 - def avgDrift(self,r=None,norm_type='standard',nd=None,warn=1):
520
521 '''
522 Calculate the average drift velocity as a function of radius,
523 normalising with respect to the average grain size (requires the
524 grain size distribution).
525
526 @keyword r: The radial points (cm). If None, the default r grid is used.
527
528 (default: None)
529 @type r: float/array
530 @keyword norm_type: The weighting of normalisation for the average drift
531 velocity:
532 - collisional: Int(w*nd*a^2*da)/Int(nd*a^2*da)
533 - dens: Int(w*nd*a^3*da)/Int(nd*a^3*da)
534 - nd: Int(w*nd*da)/Int(nd*da)
535 - a: Int(w*a*da)/Int(a*da)
536 - standard: Int(w*da)/Int(da)
537
538 (default: 'standard')
539 @type norm_type: str
540 @keyword nd: The grain size distribution. Only needed when norm_type is
541 dens or nd.
542
543 (default: None)
544 @type nd: Distribution()
545 @keyword warn: Warn when extrapolation occurs.
546
547 (default: 1)
548 @type warn: bool
549
550 @return: The average drift velocity evaluated at r (cm/s). Array is
551 reduced by one dimension since a drops out as variable.
552 @rtype: array/float
553
554 '''
555
556 if self.a.size == 1:
557 return np.squeeze(self.eval(r,warn=warn))
558
559 norm_type = norm_type.lower()
560
561 if norm_type == 'a':
562 wsum = trapz(y=self.eval(x=r,warn=warn)*self.a,x=self.a,axis=1)
563 norm = trapz(y=self.a*self.a,x=self.a)
564
565
566 elif norm_type == 'collisional':
567
568 cs_tot = nd.eval(x=r,y=self.a,warn=warn)*self.a**2
569 wsum = trapz(y=self.eval(x=r,warn=warn)*cs_tot,x=self.a,axis=1)
570 norm = trapz(y=cs_tot,x=self.a,axis=1)
571
572
573 elif norm_type == 'nd':
574 ndens = nd.eval(r,self.a,warn=warn)
575 wsum = trapz(y=self.eval(x=r,warn=warn)*ndens,x=self.a,axis=1)
576 norm = trapz(y=ndens,x=self.a,axis=1)
577
578
579 elif norm_type == 'dens':
580
581 rho = nd.eval(r,self.a,warn=warn)*self.a**3
582 wsum = trapz(y=self.eval(x=r,warn=warn)*rho,x=self.a,axis=1)
583 norm = trapz(y=rho,x=self.a,axis=1)
584
585
586 else:
587 wsum = trapz(y=self.eval(x=r,warn=warn),x=self.a,axis=1)
588 norm = trapz(y=np.ones_like(self.a),x=self.a)
589
590 return wsum/norm
591