1
2
3 """
4 Module for calculating the grain size distribution.
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 Velocity, Mdot
17
18
19 -def MRN(r,a,w,v,mdot_dust,sd,a_min=0.005e-4,a_max=0.25e-4,returnA=0):
20
21 '''
22 Calculate the MRN grain size distribution for an AGB wind.
23
24 This assumes an average drift velocity independent of grain size, and a
25 constant dust-to-gas ratio throughout the wind.
26
27 The MRN distribution is taken from Mathis, Rumpl and Nordsieck (1977), and
28 reads:
29 n_d(a,r) = A(r) n_Htot(r) a^-3.5
30
31 A(r) is a scaling factor that is derived from the dust mass-loss rate. This
32 causes the H-number density to drop out of the equation, so this information
33 is not needed:
34 A(r) = md / [nHtot 16/3 pi^2 sd r^2 Int((vg + w) a^-0.5 da)]
35
36 The MRN distribution assumes minimum grain size of .005 micron and maximum
37 grain size of 0.25 micron. While the method allows going outside this range
38 (e.g. such as in the case of calculating a local value just outside the
39 range when solving a differential equation), it is recommended changing them
40 if the grain size distribution extends beyond it. These are the values used
41 to derive A(r).
42
43 @param r: The radial grid (cm)
44 @type r: array
45 @param a: The grain size grid (cm)
46 @type a: array
47 @param w: The drift velocity profile (cm/s) as a function of r and a.
48 @type w: Drift()
49 @param v: The gas velocity (cm/s). Either as a profile, or as a cst
50 @type v: float/Velocity()
51 @param mdot_dust: The dust mass-loss rate (msun/yr). Either as a profile, or
52 as a cst
53 @type mdot_dust: float/Mdot()
54 @param sd: The average specific density of the dust grains in g/cm3.
55 @type sd: float
56
57 @keyword a_min: The minimum grain size in cm for MRN
58
59 (default: 0.005e-4)
60 @type a_min: float
61 @keyword a_max: The maximum grain size in cm for MRN
62
63 (default: 0.25e-4)
64 @type a_max: float
65 @keyword returnA: Return the scale factor separately as well. This is the
66 second element of the tuple. Note that this does not
67 contain Htot, ie A = A_local/nhtot
68
69 (default: 0)
70 @type returnA: bool
71
72 @return: The grain size distribution as a function of a
73 @rtype: array
74
75 '''
76
77
78 mddi = mdot_dust.eval(r) if isinstance(mdot_dust,Mdot.Mdot) else mdot_dust
79 mddi = (mddi*u.Msun/u.yr).to(u.g/u.s).value
80 vi = v.eval(r) if isinstance(v,Velocity.Velocity) else v
81
82
83
84 term1 = 2*(np.sqrt(a_max)-np.sqrt(a_min))*vi
85 term2 = trapz(x=w.a,y=w.eval(x=r)*w.a**-0.5)
86
87
88 denominator = 16*np.pi**2*sd*r**2*(term1+term2)
89 numerator = mddi*3.
90 A = numerator/denominator
91
92
93
94
95 nd = np.outer(A,a**-3.5)
96
97 if returnA: return (nd,A)
98
99 return nd
100
101
102
104
105 '''
106 An interface for a grain size distribution as a function of grain size and
107 radius, and for given minimum and maximum grain sizes.
108
109 '''
110
111 - def __init__(self,r,a,func,*args,**kwargs):
112
113 '''
114 Create an instance of the Distribution() class. Requires a radial
115 and a grain size grid.
116
117 The function can also be given as an interpolation object.
118
119 The optional args and kwargs give the additional arguments for the
120 temperature function, which are ignored in case func is an interpolation
121 object.
122
123 @param r: The radial points (cm)
124 @type r: array
125 @param a: The grain size points (cm). Must include minimum and maximum
126 grain sizes, unless manually declared in kwargs.
127 @type a: array
128 @param func: The function that describes the grain size distribution.
129 Can be given as an interpolation object.
130 @type func: function/interpolation object
131
132 @keyword args: Additional parameters passed to the functions when eval
133 or diff are called.
134
135 (default: [])
136 @type args: tuple
137 @keyword kwargs: Additional keywords passed to the functions when eval
138 or diff are called.
139
140 (default: {})
141 @type kwargs: dict
142
143 '''
144
145
146 if not kwargs.has_key('a_min'):
147 kwargs['a_min'] = a[0]
148 if not kwargs.has_key('a_max'):
149 kwargs['a_max'] = a[-1]
150
151
152 super(Distribution, self).__init__(r,a,func,*args,**kwargs)
153
154 self.r = self.x
155 self.a = self.y
156 self.nd = self.z
157
158
159
161
162 '''
163 Evaluate the profile function at a coordinate point.
164
165 Passes all args and kwargs on the call to self.eval().
166
167 @return: The profile evaluated at x and y
168 @rtype: array/float
169
170 '''
171
172 return self.eval(*args,**kwargs)
173
174
175
176 - def eval(self,x=None,y=None,warn=1,w_sputter=0.,w=np.empty(0)):
177
178 '''
179 Evaluate the grain-size distribution function at a coordinate point.
180
181 x/y can be any value or array. If func is an interpolation object, it is
182 in principle limited by the x/y-range of the interpolator. It is advised
183 not to extend much beyond the given x/y-range.
184
185 If one of the two variables is None, it is replaced by the default grid.
186
187 Sputtering can be applied here by passing the drift array and the max
188 drift velocity to the call. Any grain sizes with a drift above w_sputter
189 will have their number density set to 0. This depends on both the grain
190 size and the radius.
191
192 @keyword x: The primary coordinate point(s). If None, the default
193 coordinate grid is used.
194
195 (default: None)
196 @type x: array/float
197 @keyword y: The secondary coordinate point(s). If None, the default
198 coordinate grid is used.
199
200 (default: None)
201 @type y: array/float
202 @keyword warn: Warn when extrapolation occurs.
203
204 (default: 1)
205 @type warn: bool
206 @keyword w_sputter: The sputtering drift velocity above which the number
207 density is set to zero. Default in case no
208 sputtering is to be applied.
209
210 (default: 0.)
211 @type w_sputter: float
212 @keyword w: The Drift() object. Only relevant when w_sputter is nonzero.
213
214 (default: np.empty(0))
215 @type w: array
216
217 @return: The profile evaluated at x and y
218 @rtype: array/float
219
220 '''
221
222
223 z = super(Distribution,self).eval(x=x,y=y,warn=warn)
224
225
226 if w_sputter == 0.: return z
227
228
229
230 z = np.where(w>w_sputter,np.zeros_like(z),z)
231 return z
232