1
2
3 """
4 Tools for making and managing opacity files.
5
6 Author: R. Lombaert
7
8 """
9
10 from scipy import array,argmin
11 from glob import glob
12 from math import pi
13 import os
14 import types
15
16 import cc.path
17 from cc.tools.io import DataIO
18 from cc.tools.numerical import Interpol
19
20
22
23 '''
24 Calculate the mass fraction of a subset of grain sizes from a grain size
25 distribution.
26
27 A minimum of two subsets is created, so two masses are returned. Can be
28 anything higher than that, depending on how many cuts are requested.
29
30 @param acut: The grain size(s) at which the cuts are made. Can be a float
31 or a list.
32 @type acut: list(float)
33
34 @keyword amin: The minimum grain size in the distribution
35
36 (default: 0.01)
37 @type amin: float
38 @keyword amax: The maximum grain size in the distribution
39
40 (default: 100)
41 @type amax: float
42 @keyword slope: The slope of the a-dependence of the distribution. Default
43 is the MRN value of -3.5
44
45 (default: -3.5)
46 @type slope: float
47
48 @return: The mass fraction of the first slice, the second slice, etc,
49 depending on how many cuts are requested. Minimum of two.
50 @rtype: [float,float,...]
51
52 '''
53
54 if not type(acut) is types.ListType:
55 acut = [float(acut)]
56 acut = sorted(acut)
57
58 if slope == -4.: return np.empty(0)
59
60 p = (3.+slope)+1.
61 subsets = []
62
63 subsets.append(acut[0]**p-amin**p)
64
65 for i in xrange(len(acut[1:])):
66 subsets.append(acut[i+1]**p-acut[i]**p)
67
68 subsets.append(amax**p-acut[-1]**p)
69
70
71 fractions = array(subsets)/sum(subsets)
72 return fractions
73
74
75
76 -def mergeOpacity(species,lowres='nom_res',highres='high_res'):
77
78 '''
79 Merge high-res opacities into a grid of low-res opacities.
80
81 The wavelength range of the inserted high res opacities is taken from the
82 given high res grid.
83
84 @param species: The dust species for which this is done. This is also the
85 name of the folder in ~/MCMax/DustOpacities/ that contains
86 the data files.
87 @type species: string
88 @keyword lowres: The subfolder in ~/MCMax/DustOpacities/species containing
89 the low resolution datafiles.
90
91 (default: low_res)
92 @type lowres: string
93 @keyword highres: The subfolder in ~/MCMax/DustOpacities/species containing
94 the high resolution datafiles.
95
96 (default: high_res)
97 @type highres: string
98
99 '''
100
101 path = os.path.join(cc.path.mopac,species)
102 lowres_files = [f
103 for f in glob(os.path.join(path,lowres,'*'))
104 if f[-5:] == '.opac']
105 highres_files = [f
106 for f in glob(os.path.join(path,highres,'*'))
107 if f[-5:] == '.opac']
108 files = set([os.path.split(f)[1] for f in lowres_files] + \
109 [os.path.split(f)[1] for f in highres_files])
110
111 for f in files:
112 hdfile = os.path.join(path,highres,f)
113 ldfile = os.path.join(path,lowres,f)
114 if os.path.isfile(ldfile) and os.path.isfile(hdfile):
115 hd = DataIO.readCols(hdfile)
116 ld = DataIO.readCols(ldfile)
117 hdw = hd[0]
118 ldw = ld[0]
119 wmin = hdw[0]
120 wmax = hdw[-1]
121 ld_low = [list(col[ldw<wmin]) for col in ld]
122 ld_high = [list(col[ldw>wmax]) for col in ld]
123 hd = [list(col) for col in hd]
124 merged = [ld_low[i] + hd[i] + ld_high[i]
125 for i in range(len(hd))]
126 DataIO.writeCols(filename=os.path.join(path,f),cols=merged)
127
128
130 """
131 An interface for creating custom opacity files by taking the original and
132 tinkering with it.
133
134 """
135
137
138 """
139 Initializing an instance of the custom opacities interface.
140
141 @param species: the species short name
142 @type species: string
143
144 """
145
146 self.setSpecies(species)
147 self.opacity_file = False
148
149
151
152 """
153 Change the species of the current CustomOpacity instance.
154
155 @param species: the species short name
156 @type species: string
157
158 """
159
160 self.species = species
161 self.index = DataIO.getInputData(keyword='SPECIES_SHORT',\
162 filename='Dust.dat')\
163 .index(self.species)
164 self.filename = DataIO.getInputData(keyword='PART_FILE',\
165 filename='Dust.dat',\
166 rindex=self.index)
167 fn = os.path.join(cc.path.mopac,self.filename)
168 self.input_data = DataIO.readFile(filename=fn,delimiter=' ')
169
170
171
172 - def makeOpa(self,mode='ZERO',**args):
173
174 """
175 Making custom .particle files.
176
177 Every method called here will put the results in self.output_data.
178
179 @keyword mode: type of extrapolation (ZERO,FUNCTION,HONY,ZHANG)
180
181 (default: 'ZERO')
182 @type mode: string
183 @keyword args: Optional keywords required for the other methods of the
184 class
185 @type args: dict
186
187 """
188
189 self.output_data = []
190 mode = mode.upper()
191 if hasattr(self,'do' + mode):
192 getattr(self,'do' + mode)(**args)
193 self.output_data = [' '.join(str(line))
194 for line in self.output_data]
195 output_filename = '_'.join(['customOpacity',mode] + \
196 sorted(args.values()) + \
197 [self.filename])
198 if self.opacity_file:
199 output_filename.replace('.particle','.opacity')
200 DataIO.writeFile(filename=os.path.join(cc.path.mopac,\
201 output_filename),\
202 input_lines=self.output_data)
203 new_short = self.species + mode
204
205
206
207 try:
208 DataIO.getInputData(keyword='PART_FILE',filename='Dust.dat')\
209 .index(output_filename)
210
211
212 except ValueError:
213 i=0
214 while ' '.join(DataIO.getInputData(keyword='SPECIES_SHORT',\
215 filename='Dust.dat'))\
216 .find(new_short) != -1:
217 i+=1
218 new_short = new_short + str(i)
219 adding_line = [new_short] + \
220 [str(DataIO.getInputData(keyword=key,\
221 filename='Dust.dat',\
222 rindex=self.index))
223 for key in ['SPEC_DENS','T_DES','T_DESA','T_DESB']]
224 adding_line.insert(2,output_filename)
225 adding_line = '\t\t'.join(adding_line)
226 DataIO.writeFile(os.path.join(cc.path.usr,'Dust.dat'),\
227 [adding_line+'\n'],mode='a')
228 else:
229 print 'Mode "' + mode + '" not available. Aborting.'
230
231
232
234
235 """
236 Replace all opacities by zero below a cut-off wavelength.
237
238 @keyword wl: the wavelength of the discontinuity to zero
239
240 (default: 10.0)
241 @type wl: float
242
243 """
244
245 for line in self.input_data:
246 if len(line) == 4 and float(line[0]) < wl:
247 self.output_data.append([line[0],str(2e-60),str(1e-60),str(1e-60)])
248 else:
249 self.output_data.append(line)
250
251
252
253 - def doFUNCTION(self,wl_min=10.0,wl_max=20.0,function='linear'):
254
255 """
256 Replace all opacities by an extrapolation below a cut-off wavelength.
257
258 @keyword wl_min: lower boundary interpolation range and the cutoff
259 wavelength for the extrapolation
260
261 (default: 10.0)
262 @type wl_min: float
263 @keyword wl_max: upper boundary interpolation range
264
265 (default: 20.0)
266 @type wl_max: float
267 @keyword function: type of function used for the inter/extrapolation
268 See Interpol.pEval for function types
269
270 (default: 'linear')
271 @type function: string
272
273 """
274
275
276
277
278 self.opacity_file = True
279 inputsel = [line for line in self.input_data if len(line) == 4]
280 inputsel = array([[float(f) for f in line] for line in inputsel])
281 wl = inputsel[:,0]
282 function = function.lower()
283
284
285 wl_low = wl[wl < wl_min]
286 wlinter = wl[(wl >= wl_min) * (wl <= wl_max)]
287 abs_inter = inputsel[:,2][(wl >= wl_min) * (wl <= wl_max)]
288 sca_inter = inputsel[:,3][(wl >= wl_min) * (wl <= wl_max)]
289
290
291 abs_p0 = [abs_inter[0],wl[0],1,1]
292 sca_p0 = [sca_inter[0],wl[0],1,1]
293 abs_low = Interpol.fitFunction(x_in=wlinter,y_in=abs_inter,\
294 x_out=wl_low,func=function,\
295 initial=abs_p0,show=1)
296 abs_low = array([a > 0 and a or 0 for a in abs_low])
297 sca_low = Interpol.fitFunction(x_in=wlinter,y_in=sca_inter,\
298 x_out=wl_low,func=function,\
299 initial=sca_p0,show=1)
300 sca_low = array([s > 0 and s or 0 for s in sca_low])
301 ext_low = abs_low + sca_low
302
303
304 self.output_data = [array([w,el,al,sl])
305 for w,el,al,sl in zip(wl_low,ext_low,\
306 abs_low,sca_low)]
307 self.output_data.extend(inputsel[wl >= wl_min])
308
309
310
311 - def doHONY(self,wl1 = 1.0,wl2=2.0,wl3=10.0,a_mod=0.05,q_cst=1.0):
312
313 """
314 Replace all opacities below a cut-off wavelength with values as assumed
315 by Hony et al (2003, 2004)
316
317 @keyword wl1: first discontinuity in micron
318
319 (default: 1.0)
320 @type wl1: float
321 @keyword wl2: second discontinuity in micron
322
323 (default: 2.0)
324 @type wl2: float
325 @keyword wl3: third discontinuity in micron
326
327 (default: 10)
328 @type wl3: float
329 @keyword a_mod: model grain size, 0.01 according to Hony 2004 in micron
330
331 (default: 0.05)
332 @type a_mod: float
333 @keyword q_cst: constant extinction efficiency at wavelengths < wl1
334
335 (default: 1.0)
336 @type q_cst: float
337
338 """
339
340 spec_dens = DataIO.getInputData(keyword='SPEC_DENS',rindex=self.index,\
341 filename='Dust.dat')
342 opa_cst = q_cst/4.0*3.0/spec_dens/(a_mod*10**(-4))
343 for line in self.input_data:
344 if len(line) == 4 and float(line[0]) < wl1:
345 self.output_data.append([line[0],str(opa_cst+1e-60),\
346 str(opa_cst),str(1e-60)])
347 elif len(line)==4 and float(line[0])>=wl1 and float(line[0]) < wl2:
348 opa = Interpol.linInterpol([wl1,wl2],[opa_cst,1e-60],\
349 float(line[0]))
350 self.output_data.append([line[0],str(opa+1e-60),str(opa),\
351 str(1e-60)])
352 elif len(line)==4 and float(line[0])>=wl2 and float(line[0]) < wl3:
353 self.output_data.append([line[0],str(2e-60),str(1e-60),\
354 str(1e-60)])
355 else:
356 self.output_data.append(line)
357
358
359
360 - def doZHANG(self,wl=10.0,a_mod=0.05,q_cst=1.6,metallic=True):
361
362 """
363 Replace all opacities below a cut-off wavelength with values as assumed
364 by Zhang et al (2009)
365
366 @keyword wl: wavelength of the discontinuity
367
368 (default: 10.0)
369 @type wl: float
370 @keyword a_mod: model grain size, following Zhang et al 2009
371
372 (default: 0.05)
373 @type a_mod: float
374 @keyword q_cst: the constant extinction efficieny at short wavelengths
375
376 (default: 1.6)
377 @type q_cst: float
378 @keyword metallic: if metallic dust, cut off wavelength is calculated
379 differently than if dielectric dust (ie metallic=0)
380
381 (default: True)
382 @type metallic: bool
383
384 """
385
386 spec_dens = DataIO.getInputData(keyword='SPEC_DENS',rindex=self.index,\
387 filename='Dust.dat')
388 wl1 = metallic and pi*a_mod or 2*pi*a_mod
389 opa_cst = q_cst/4.0*3.0/spec_dens/(a_mod*10**(-4))
390 i = 0
391 while float(self.input_data[i][0]) <= wl:
392 i += 1
393 opa_10 = float(self.input_data[i][1])
394 for line in self.input_data:
395 if len(line) == 4 and float(line[0]) <= wl1:
396 self.output_data.append([line[0],str(opa_cst+1e-60),\
397 str(opa_cst),str(1e-60)])
398 elif len(line)==4 and float(line[0]) > wl1 and float(line[0]) < wl:
399 eps = min(1,float(line[0])/wl)
400 opa = (1-eps) * opa_cst*wl1/float(line[0]) + eps*opa_10
401
402 self.output_data.append([line[0],str(opa+1e-60),str(opa),\
403 str(1e-60)])
404 else:
405 self.output_data.append(line)
406