1 import os
2 import copy
3 import numpy as np
4 from scipy import interpolate
5 import time
6
7 import chianti.data as chdata
8 import pylab as pl
9
10 import chianti.filters as chfilters
11 import chianti.util as util
12 import chianti.io as io
13 import chianti.constants as const
14 import chianti.Gui as chGui
15 from ._IonTrails import _ionTrails
16 from ._SpecTrails import _specTrails
17
18 xuvtop = chdata.xuvtop
19 heseqLvl2 = [-1,3,-1,-1,-1,5,6,6,-1,6,6,6,5,5,3,5,3,5,3,5,-1,-1,-1,-1,-1,4,-1,4,-1,4]
20
21 -class ion(_ionTrails, _specTrails):
22 '''
23 The top level class for performing spectral calculations for an ion in the CHIANTI database.
24
25 ionStr is a string corresponding such as 'c_5' that corresponds to the C V ion.
26 temperature in Kelvin
27 eDensity in cm^-3
28 radTemperature, the radiation black-body temperature in Kelvin
29 rPlot, the distance from the center of the star in stellar radii
30
31 Inherited methods include 'intensityList', 'intensityRatio' (between lines of different ions), 'intensityRatioSave'
32 and 'convolve'
33 '''
34 - def __init__(self, ionStr, temperature=None, eDensity=None, pDensity='default', radTemperature=0, rStar=0, abundanceName=0, abundance=0, verbose=0, setup=True, em=0):
35 '''
36 The top level class for performing spectral calculations for an ion in the CHIANTI database.
37
38 ionStr is a string corresponding such as 'c_5' that corresponds to the C V ion.
39 temperature in Kelvin
40 eDensity in cm^-3
41 radTemperature, the radiation black-body temperature in Kelvin
42 rStar, the distance from the center of the star in stellar radii
43
44 the elemental abundance values can be set to one of the names in the XUVTOP/abundance directory
45 without the '.abund' suffix, such as abundanceName = 'sun_photospheric_1998_grevesse'
46
47 '''
48
49
50
51 self.IonStr=ionStr
52 self.Z=util.convertName(ionStr)['Z']
53 self.Ion=util.convertName(ionStr)['Ion']
54 self.Dielectronic=util.convertName(ionStr)['Dielectronic']
55 self.Spectroscopic=util.zion2spectroscopic(self.Z,self.Ion)
56 self.FileName=util.zion2filename(self.Z, self.Ion,dielectronic=self.Dielectronic )
57
58 self.Defaults=chdata.Defaults
59
60 if abundance:
61 self.Abundance=abundance
62 elif abundanceName:
63 if abundanceName in chdata.AbundanceList:
64 self.AbundanceName = abundanceName
65 self.Abundance = chdata.Abundance[self.AbundanceName]['abundance'][self.Z-1]
66 else:
67 abundChoices = chdata.AbundanceList
68
69
70 abundChoice = chGui.gui.selectorDialog(abundChoices,label='Select Abundance name')
71 abundChoice_idx = abundChoice.selectedIndex
72 self.AbundanceName = abundChoices[abundChoice_idx[0]]
73 self.Abundance = chdata.Abundance[self.AbundanceName]['abundance'][self.Z-1]
74 print(' Abundance chosen: %s '%(self.AbundanceName))
75 else:
76 self.AbundanceName = self.Defaults['abundfile']
77 self.Abundance = chdata.Abundance[self.AbundanceName]['abundance'][self.Z-1]
78
79
80 self.IoneqName = self.Defaults['ioneqfile']
81
82
83
84 self.RadTemperature = radTemperature
85 self.RStar = rStar
86
87
88 if self.Ion <= self.Z:
89 self.Ip=chdata.Ip[self.Z-1, self.Ion-1-self.Dielectronic]
90 self.FIP = chdata.Ip[self.Z-1, 0]
91 if self.Dielectronic:
92 self.UpperIp=chdata.Ip[self.Z-1, self.Ion-1]
93
94 if type(temperature) != type(None):
95 self.Temperature = np.array(temperature,'float64')
96 self.NTemp = self.Temperature.size
97
98
99
100
101
102 self.IoneqAll = chdata.IoneqAll
103 self.ioneqOne()
104
105
106 if pDensity == 'default':
107 self.p2eRatio()
108
109 if type(eDensity) != type(None):
110 self.EDensity = np.asarray(eDensity,'float64')
111 self.NEDens = self.EDensity.size
112 ndens = self.EDensity.size
113 ntemp = self.Temperature.size
114 tst1 = ndens == ntemp
115 tst1a = ndens != ntemp
116 tst2 = ntemp > 1
117 tst3 = ndens > 1
118 tst4 = ndens > 1 and ntemp > 1
119 if tst1 and ntemp == 1:
120 self.NTempDen = 1
121 elif tst1a and (tst2 or tst3) and not tst4:
122 self.NTempDen = ntemp*ndens
123 if ntemp == self.NTempDen and ndens != self.NTempDen:
124 self.EDensity = np.ones_like(self.Temperature)*self.EDensity
125 elif ndens == self.NTempDen and ntemp != self.NTempDen:
126 self.Temperature = np.ones_like(self.EDensity)*self.Temperature
127 elif tst1 and tst4:
128 self.NTempDen = ntemp
129
130 if pDensity == 'default' and type(eDensity) != type(None):
131 if tst1 and tst2 and tst3:
132 self.PDensity = np.zeros((ntemp), 'float64')
133 for itemp in range(ntemp):
134 self.PDensity[itemp] = self.ProtonDensityRatio[itemp]*self.EDensity[itemp]
135 elif tst2 and tst3 and not tst1:
136 print(' if both temperature and eDensity are arrays, they must be of the same size')
137 return
138 else:
139 self.PDensity = self.ProtonDensityRatio*self.EDensity
140 else:
141 self.PDensity = pDensity
142 if setup:
143 if self.IonStr in chdata.MasterList:
144 self.setup()
145 else:
146 self.setupIonrec()
147
148 if type(em) == int and em == 0:
149
150
151
152
153
154 pass
155 elif type(em) == float and em > 0.:
156 em = np.ones(self.NTempDen, 'float64')*em
157 self.Em = em
158 elif type(em) == list or type(em) == tuple or type(em) == np.ndarray:
159 em = np.asarray(em, 'float64')
160 self.Em = em
161
162
163
164 - def diCross(self, energy=None, verbose=False):
165 '''
166 Calculate the direct ionization cross section.
167
168 Given as a function of the incident electron energy in eV, puts values into DiCross
169 '''
170 iso = self.Z - self.Ion + 1
171 if type(energy) == type(None):
172 btenergy=0.1*np.arange(10)
173 btenergy[0]=0.01
174 dum=np.ones(len(btenergy))
175 [energy, dum]=util.descale_bti(btenergy, dum, 2., self.Ip)
176 energy=np.asarray(energy, 'float64')
177
178 if iso == 1 and self.Z >= 6:
179
180 ryd=27.2113845/2.
181 u=energy/self.Ip
182 ev1ryd=self.Ip/ryd
183 a0=0.5291772108e-8
184 a_bohr=const.pi*a0**2
185 if self.Z >= 20:
186 ff = (140.+(self.Z/20.)**3.2)/141.
187 else:
188 ff = 1.
189 qr = util.qrp(self.Z,u)*ff
190 bb = 1.
191 qh = bb*a_bohr*qr/ev1ryd**2
192 self.DiCross = {'energy':energy, 'cross':qh}
193 elif iso == 2 and self.Z >= 10:
194
195 ryd=27.2113845/2.
196 u=energy/self.Ip
197 ev1ryd=self.Ip/ryd
198 a0=0.5291772108e-8
199 a_bohr=const.pi*a0**2
200 if self.Z >= 20:
201 ff=(140.+(self.Z/20.)**3.2)/141.
202 else:
203 ff=1.
204 qr=util.qrp(self.Z,u)*ff
205 bb=2.
206 qh=bb*a_bohr*qr/ev1ryd**2
207 self.DiCross={'energy':energy, 'cross':qh}
208 else:
209
210
211
212
213
214 if not hasattr(self, 'DiParams'):
215 self.DiParams = io.diRead(self.IonStr)
216
217 cross=np.zeros(len(energy), 'Float64')
218
219 for ifac in range(self.DiParams['info']['nfac']):
220
221 goode=energy > self.DiParams['ev1'][ifac]
222 if goode.sum() > 0:
223 dum=np.ones(len(energy))
224 btenergy, btdum=util.scale_bti(energy[goode],dum[goode], self.DiParams['btf'][ifac], self.DiParams['ev1'][ifac] )
225
226 y2=interpolate.splrep(self.DiParams['xsplom'][ifac], self.DiParams['ysplom'][ifac], s=0)
227 btcross=interpolate.splev(btenergy, y2, der=0)
228 energy1, cross1=util.descale_bti(btenergy, btcross,self.DiParams['btf'][ifac], self.DiParams['ev1'][ifac] )
229 offset=len(energy)-goode.sum()
230 if verbose:
231 pl.plot(self.DiParams['xsplom'][ifac], self.DiParams['ysplom'][ifac])
232 pl.plot(btenergy, btcross)
233 if offset > 0:
234 seq=[np.zeros(offset, 'Float64'), cross1]
235 cross1=np.hstack(seq)
236 cross += cross1*1.e-14
237 self.DiCross={'energy':energy, 'cross':cross}
238
239
240
242 '''
243 Calculate the direct ionization rate coefficient as a function of temperature (K)
244 '''
245
246
247
248
249
250 if hasattr(self, 'Temperature'):
251 temperature=self.Temperature
252 else:
253 print(' temperature is not defined')
254 return
255
256
257
258 ngl=12
259 xgl=np.asarray([0.115722117358021,0.611757484515131,1.512610269776419,2.833751337743509
260 ,4.599227639418353,6.844525453115181,9.621316842456871,13.006054993306350
261 ,17.116855187462260,22.151090379396983,28.487967250983992,37.099121044466926], 'float64')
262
263
264 wgl=np.asarray([2.647313710554435e-01,3.777592758731382e-01,2.440820113198774e-01,9.044922221168074e-02
265 ,2.010238115463406e-02,2.663973541865321e-03,2.032315926629993e-04,8.365055856819753e-06
266 ,1.668493876540914e-07,1.342391030515027e-09,3.061601635035012e-12,8.148077467426124e-16], 'float64')
267
268 alpha=5.287e+13
269 tev=const.boltzmannEv*temperature
270
271 ntemp=temperature.size
272
273
274 if ntemp == 1:
275 x0=self.Ip/tev
276 beta=np.sqrt(const.boltzmann*temperature)
277 egl=self.Ip+xgl*tev
278 self.diCross(energy=egl)
279 crossgl=self.DiCross['cross']
280 term1=wgl*xgl*crossgl
281 term2=wgl*crossgl
282 newcross=alpha*beta*np.exp(-x0)*(term1.sum()+x0*term2.sum())
283 rate=newcross
284 else:
285 rate=np.zeros(ntemp, 'float64')
286 for itemp in range(ntemp):
287 x0=self.Ip/tev[itemp]
288 beta=np.sqrt(const.boltzmann*temperature[itemp])
289 egl=self.Ip+xgl*tev[itemp]
290 self.diCross(energy=egl)
291 crossgl=self.DiCross['cross']
292 term1=wgl*xgl*crossgl
293 term2=wgl*crossgl
294 newcross=alpha*beta*np.exp(-x0)*(term1.sum()+x0*term2.sum())
295 rate[itemp]=newcross
296 self.DiRate={'temperature':temperature, 'rate':rate}
297
298
299
301 """
302 Calculates the effective collision strengths (upsilon)
303 for excitation-autoionization as a function of temperature.
304 """
305
306
307
308
309
310 if hasattr(self, 'EaParams'):
311 eaparams=self.EaParams
312 else:
313 self.EaParams = io.eaRead(self.IonStr)
314 eaparams=self.EaParams
315
316 if hasattr(self, 'Temperature'):
317 temperature=self.Temperature
318 else:
319 print(' temperature is not defined')
320 return
321 ntemp=temperature.size
322 nsplups=len(eaparams['de'])
323 if ntemp > 1:
324 ups=np.zeros((nsplups,ntemp),"Float64")
325 else:
326 ups=np.zeros(nsplups,"Float64")
327
328 for isplups in range(0,nsplups):
329 l1=self.EaParams["lvl1"][isplups]-1
330 l2=self.EaParams["lvl2"][isplups]-1
331 ttype=self.EaParams["ttype"][isplups]
332 cups=self.EaParams["cups"][isplups]
333 nspl=self.EaParams["nspl"][isplups]
334 de=self.EaParams["de"][isplups]
335 dx=1./(float(nspl)-1.)
336
337 splups=self.EaParams["splups"][isplups,0:nspl]
338 kte=const.boltzmannEv*temperature/(const.ryd2Ev*de)
339
340 if ttype ==1:
341 st=1.-np.log(cups)/np.log(kte+cups)
342 xs=dx*np.arange(nspl)
343 y2=interpolate.splrep(xs,splups,s=0)
344 sups=interpolate.splev(st,y2,der=0)
345 ups[isplups]=sups*np.log(kte+np.exp(1.))
346
347 if ttype == 2:
348 st=kte/(kte+cups)
349 xs=dx*np.arange(nspl)
350 y2=interpolate.splrep(xs,splups,s=0)
351 sups=interpolate.splev(st,y2,der=0)
352 ups[isplups]=sups
353
354 if ttype == 3:
355 st=kte/(kte+cups)
356 xs=dx*np.arange(nspl)
357 y2=interpolate.splrep(xs,splups,s=0)
358 sups=interpolate.splev(st,y2,der=0)
359 ups[isplups]=sups/(kte+1.)
360
361 if ttype == 4:
362 st=1.-np.log(cups)/np.log(kte+cups)
363 xs=dx*np.arange(nspl)
364 y2=interpolate.splrep(xs,splups,s=0)
365 sups=interpolate.splev(st,y2,der=0)
366 ups[isplups]=sups*np.log(kte+cups)
367
368 if ttype == 5:
369 st=kte/(kte+cups)
370 xs=dx*np.arange(nspl)
371 y2=interpolate.splrep(xs,splups,s=0)
372 sups=interpolate.splev(st,y2,der=0)
373 ups[isplups]=sups/(kte+0.)
374
375
376 elif ttype > 5: print(' t_type ne 1,2,3,4,5 = %5i %5i %5i'%(ttype,l1,l2))
377
378
379 ups=np.where(ups > 0.,ups,0.)
380
381 self.EaParams['ups']=ups
382 return ups
383
384
385
386 - def eaCross(self, energy=0, verbose=False):
387 '''
388 Provide the excitation-autoionization cross section.
389
390 Energy is given in eV.
391 '''
392
393
394
395
396
397
398
399
400 if self.DiParams['info']['neaev'] == 0:
401
402 return
403 else:
404 if not energy.all():
405 energy=self.Ip*10.**(0.05*np.arange(31))
406 try:
407 easplom=self.Easplom
408 except:
409
410 self.Easplom = util.splomRead(self.IonStr, ea=1)
411 easplom =self.Easplom
412
413
414
415
416
417 omega = util.splomDescale(easplom, energy)
418
419
420 ntrans=len(easplom['deryd'])
421 nsplom=easplom['splom'].shape[1]
422 x=0.25*np.arange(nsplom)
423 eaev=self.DiParams['eaev']
424 if len(eaev) ==1:
425 for itrans in range(ntrans):
426 eaev.append(eaev[0])
427
428 totalCross = np.zeros_like(energy)
429 ntrans = omega.shape[0]
430 for itrans in range(ntrans):
431 lvl1 = self.Easplom['lvl1'][itrans]
432
433
434 cross = eaev[itrans]*const.bohrCross*omega[itrans]/(energy/const.ryd2Ev)
435 totalCross += cross
436 self.EaCross = {'energy':energy, 'cross':totalCross}
437 return
438
439
440
442 '''Calculate the excitation-autoionization rate coefficient.'''
443
444
445 if not hasattr(self, 'DiParams'):
446 self.DiParams = io.diRead(self.IonStr)
447
448 if self.DiParams['info']['neaev'] == 0:
449
450 return
451 else:
452 if hasattr(self, 'Temperature'):
453 temperature=self.Temperature
454 else:
455 bte=0.1*np.arange(10)
456 bte[0]=0.01
457 dum=np.ones(10, 'Float64')
458 [temperature, dum]=util.descale_bt(bte, dum, self.EaParams['cups'][0], self.DiParams['de'][0])
459 self.Temperature=temperature
460 if hasattr(self, 'EaParams'):
461 eaparams=self.EaParams
462 else:
463 self.eaParams = io.eaRead(self.IonStr)
464 self.eaDescale()
465 eaparams=self.EaParams
466
467
468 nups=len(eaparams['de'])
469 tev=const.boltzmannEv*temperature
470 earate=np.zeros(temperature.size, 'Float64')
471 eaev=self.DiParams['eaev']
472 if len(eaev) ==1:
473 for iups in range(nups):
474 eaev.append(eaev[0])
475
476 for iups in range(nups):
477 x0=const.ryd2Ev*eaparams['de'][iups]/tev
478
479
480 earate+=eaev[iups]*8.63e-6*eaparams['ups'][iups]*np.exp(-x0)/(np.sqrt(temperature))
481 self.EaRate={'rate':earate, 'temperature':temperature}
482 return
483
484
485
487 '''
488 Provides the total ionization cross section.
489
490 uses diCross and eaCross.
491 '''
492 if self.Z < self.Ion:
493
494 self.IonizRate = {'rate':np.zeros_like(self.Temperature), 'temperature':self.Temperature}
495 return
496 if not energy.all():
497 energy = self.Ip*10.**(0.025*np.arange(401))
498
499 self.diCross(energy)
500 self.eaCross(energy)
501 if self.DiParams['info']['neaev'] == 0:
502 ionizCross=self.DiCross['cross']
503 else:
504 ionizCross=self.DiCross['cross']+self.EaCross['cross']
505 self.IonizCross = {'cross':ionizCross, 'energy':energy}
506 return
507
508
509
511 '''
512 Provides the total ionization rate.
513
514 Calls diRate and eaRate.
515 '''
516 if self.Z < self.Ion:
517
518 self.IonizRate = {'rate':np.zeros_like(self.Temperature), 'temperature':self.Temperature}
519 return
520 self.diRate()
521 self.eaRate()
522 if self.DiParams['info']['neaev'] == 0:
523 ionizrate=self.DiRate['rate']
524 else:
525 ionizrate=self.DiRate['rate']+self.EaRate['rate']
526 self.IonizRate = {'rate':ionizrate, 'temperature':self.DiRate['temperature']}
527 return
528
529
530
531
532
533
535 '''Provide the radiative recombination rate coefficient as a function of temperature (K).'''
536 if hasattr(self, 'Temperature'):
537 temperature=self.Temperature
538 else:
539 print(' temperature is not defined')
540 return
541 rrparamsfile = util.ion2filename(self.IonStr) + '.rrparams'
542 if hasattr(self, 'RrParams'):
543 rrparams=self.RrParams
544 elif os.path.isfile(rrparamsfile):
545 self.RrParams = io.rrRead(self.IonStr)
546 rrparams=self.RrParams
547 else:
548 self.RrRate={'temperature':temperature, 'rate':np.zeros_like(temperature)}
549 return
550
551
552
553 if rrparams['rrtype'] == 1:
554 a=rrparams['params'][3]
555 b=rrparams['params'][4]
556 t0=rrparams['params'][5]
557 t1=rrparams['params'][6]
558 rate=a/(np.sqrt(temperature/t0)*(1.+np.sqrt(temperature/t0))**(1.-b)*(1.+np.sqrt(temperature/t1))**(1.+b))
559 self.RrRate={'temperature':temperature, 'rate':rate}
560 elif rrparams['rrtype'] == 2:
561 a=rrparams['params'][3]
562 b=rrparams['params'][4]
563 t0=rrparams['params'][5]
564 t1=rrparams['params'][6]
565 c=rrparams['params'][7]
566 t2=rrparams['params'][8]
567 b += c*np.exp(-t2/temperature)
568 rate=a/(np.sqrt(temperature/t0)*(1.+np.sqrt(temperature/t0))**(1.-b)*(1.+np.sqrt(temperature/t1))**(1.+b))
569 self.RrRate={'temperature':temperature, 'rate':rate}
570 elif rrparams['rrtype'] == 3:
571 a=rrparams['params'][2]
572 b=rrparams['params'][3]
573 rate=a/(temperature/1.e+4)**b
574 self.RrRate={'temperature':temperature, 'rate':rate}
575 else:
576 self.RrRate={'temperature':temperature, 'rate':np.zeros_like(temperature)}
577
578
579
580
582 '''Provide the dielectronic recombination rate coefficient as a function of temperature (K).
583 '''
584
585 if hasattr(self, 'Temperature'):
586 temperature=self.Temperature
587 else:
588 print(' temperature is not defined')
589 return {'errorMessage':' temperature is not defined'}
590 drparamsfile = util.ion2filename(self.IonStr) + '.drparams'
591 if hasattr(self, 'DrParams'):
592 drparams=self.DrParams
593 elif os.path.isfile(drparamsfile):
594 self.DrParams = io.drRead(self.IonStr)
595 drparams=self.DrParams
596 else:
597 self.DrRate = {'rate':np.zeros_like(temperature), 'temperature':temperature}
598 return
599
600 if drparams['drtype'] == 1:
601
602 drenergy=drparams['eparams']
603 drcoef=drparams['cparams']
604 gcoef = drenergy > 0.
605 ncoef=gcoef.sum()
606
607 rate=np.zeros(temperature.size, 'float64')
608 for icoef in range(ncoef):
609 rate += drcoef[icoef]*np.exp(-drenergy[icoef]/temperature)
610 rate=rate/temperature**1.5
611 self.DrRate={'temperature':temperature, 'rate':rate}
612 elif drparams['drtype'] == 2:
613
614 params = drparams['params']
615 adi = params[0]
616 bdi = params[1]
617 t0 = params[2]
618 t1 = params[3]
619 rate=adi*np.exp(-t0/temperature)*(1.+bdi*np.exp(-t1/temperature))/temperature**1.5
620 self.DrRate={'temperature':temperature, 'rate':rate}
621
622
623
625 '''Interpolate and extrapolate cilvl and reclvl rates.
626 lvltype must be either 'reclvl', 'cilvl' or 'rrlvl'
627 Used in level population calculations.
628 '''
629 if hasattr(self, 'Temperature'):
630 temperature=self.Temperature
631 else:
632 print(' temperature is not defined')
633 return {'errorMessage':' temperature is not defined'}
634 lvlfile = util.ion2filename(self.IonStr)+'.' + lvlType
635 if lvlType == 'reclvl':
636 if hasattr(self, 'Reclvl'):
637 lvl = self.Reclvl
638 elif os.path.isfile(lvlfile):
639
640 self.Reclvl = io.cireclvlRead(self.IonStr, '.'+lvlType)
641 lvl = self.Reclvl
642 else:
643 self.ReclvlRate = {'rate':np.zeros_like(temperature)}
644 return
645 elif lvlType == 'cilvl':
646 if hasattr(self, 'Cilvl'):
647 lvl = self.Cilvl
648 elif os.path.isfile(lvlfile):
649
650 self.Cilvl = io.cireclvlRead(self.IonStr, '.'+lvlType)
651 lvl = self.Cilvl
652 else:
653
654 return
655 elif lvlType == 'rrlvl':
656 if hasattr(self, 'Rrlvl'):
657 lvl = self.Rrlvl
658 elif os.path.isfile(lvlfile):
659
660 self.Rrlvl = io.cireclvlRead(self.IonStr, '.'+lvlType)
661 lvl = self.Rrlvl
662 else:
663 self.RrlvlRate = {'rate':np.zeros_like(temperature)}
664 return
665
666
667
668 ntemp = temperature.size
669 nlvl = len(lvl['lvl1'])
670 if ntemp == 1:
671 rate = np.zeros(( nlvl), 'float64')
672
673 if temperature > lvl['temperature'].max():
674
675 for itrans in range(nlvl):
676
677
678 rate[itrans] = lvl['rate'][itrans,-1]*(lvl['temperature'][itrans, -1]/temperature)
679 elif temperature < lvl['temperature'].min():
680
681 pass
682 else:
683 for itrans in range(nlvl):
684 lvl2 = lvl['lvl2'][itrans]
685 nrecTemp = lvl['ntemp'][itrans]
686 y2 = interpolate.splrep(np.log(lvl['temperature'][itrans][:nrecTemp]), np.log(lvl['rate'][itrans][:nrecTemp]))
687 cirec = np.exp(interpolate.splev(np.log(temperature),y2))
688 rate[itrans] = cirec.squeeze()
689 else:
690
691 rate = np.zeros(( nlvl, temperature.size), 'float64')
692
693 for itrans in range(nlvl):
694 lvl2 = lvl['lvl2'][itrans]
695 nTemp = lvl['ntemp'][itrans]
696 y2 = interpolate.splrep(np.log(lvl['temperature'][itrans, :nTemp]), np.log(lvl['rate'][itrans, :nTemp]))
697 goodLow = temperature < lvl['temperature'][itrans].min()
698 if goodLow.sum() >0:
699
700 lowT = temperature[goodLow]
701 good1 = temperature >= lvl['temperature'][itrans].min()
702 good2 = temperature <= lvl['temperature'][itrans].max()
703 realgood = np.logical_and(good1,good2)
704 if realgood.sum() > 0:
705
706 midT = temperature[realgood]
707 goodHigh = temperature > lvl['temperature'][itrans].max()
708 if goodHigh.sum() > 0:
709
710 highT = temperature[goodHigh]
711 lvl2 = lvl['lvl2'][itrans]
712 nTemp = lvl['ntemp'][itrans]
713 newRate = np.zeros(ntemp, 'float64')
714 index = 0
715 if goodLow.sum() == 1:
716
717
718 newRate[index] = 0.
719 index += 1
720 elif goodLow.sum() > 1:
721
722 for idx in range(goodLow.sum()):
723
724 newRate[index] = 0.
725 index += 1
726 if realgood.sum() == 1:
727 midRec = np.exp(interpolate.splev(np.log(midT),y2))
728 newRate[index] = midRec
729 index += 1
730 elif realgood.sum() > 1:
731 midRec = np.exp(interpolate.splev(np.log(midT),y2))
732 for idx in range(realgood.sum()):
733 newRate[index] = midRec[idx]
734 index += 1
735 if goodHigh.sum() == 1:
736
737
738 newRate[index] = 0.
739 index += 1
740 elif goodHigh.sum() > 1:
741
742 for idx in range(goodHigh.sum()):
743
744
745 newRate[index] = 0.
746 index += 1
747 rate[itrans] = newRate
748 if lvlType == 'reclvl':
749 self.ReclvlRate = {'rate':rate, 'lvl1':lvl['lvl1'], 'lvl2':lvl['lvl2'], 'temperature':temperature}
750 elif lvlType == 'cilvl':
751 self.CilvlRate = {'rate':rate, 'lvl1':lvl['lvl1'], 'lvl2':lvl['lvl2'], 'temperature':temperature}
752 elif lvlType == 'rrlvl':
753 self.RrlvlRate = {'rate':rate, 'lvl1':lvl['lvl1'], 'lvl2':lvl['lvl2'], 'temperature':temperature}
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
864 '''
865 to calculate the level resolved dielectronic rate from the higher ionization stage to
866 the ion of interest
867 rates are determined from autoionizing A-values
868 the dictionary self.DrRateLvl contains
869 rate = the dielectronic rate into an autoionizing level
870 effRate = the dielectronic rate into an autoionizing level mutilplied by the branching
871 ratio for a stabilizing transition
872 totalRate = the sum of all the effRates
873 '''
874 if not hasattr(self, 'Higher'):
875 nameStuff = util.convertName(self.IonStr)
876 z = nameStuff['Z']
877 stage = nameStuff['Ion']
878 higherStr = util.zion2name(z, stage+1)
879 self.Higher = ion(higherStr, self.Temperature, self.EDensity)
880
881
882
883 coef2 = (const.planck)**3/(2.*const.pi*const.emass*const.boltzmann*self.Temperature)**1.5
884
885
886 coef = coef2
887
888 nt = self.Temperature.size
889 allRate = []
890 effRate = []
891 de = []
892 erg = []
893 ipErg = self.Ip*const.ev2Erg
894 lvl = []
895 branch = []
896 dekt = []
897 totalRate = np.zeros(nt, 'float64')
898
899 for i, avalue in enumerate(self.Auto['avalue']):
900 elvl1idx = self.Elvlc['lvl'].index(self.Auto['lvl1'][i])
901 elvl2idx = self.Elvlc['lvl'].index(self.Auto['lvl2'][i])
902 gUpper = float(self.Elvlc['mult'][elvl2idx])
903 gLower = float(self.Higher.Elvlc['mult'][elvl1idx])
904
905 ecm2 = self.Elvlc['ecm'][elvl2idx]
906 if ecm2 < 0.:
907 ecm2 = self.Elvlc['ecmth'][elvl2idx]
908 de1 = ecm2*const.invCm2Erg - self.Ip*const.ev2Erg
909 erg.append(ecm2*const.invCm2Erg)
910
911 de.append(de1)
912 dekt1 = de1/(const.boltzmann*self.Temperature)
913 dekt.append(dekt1)
914
915
916 expkt = np.exp(-dekt1)
917 rate = coef*gUpper*expkt*avalue/(2.*gLower)
918 branch1 = self.Wgfa['avalueLvl'][elvl2idx]/(avalue + self.Wgfa['avalueLvl'][elvl2idx])
919 branch.append(branch1)
920 lvl.append(self.Auto['lvl2'][i])
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937 allRate.append(rate)
938 effRate.append(rate*branch1)
939 totalRate += rate*branch1
940
941
942 self.DrRateLvl = {'rate':allRate, 'effRate':effRate, 'totalRate':totalRate, 'de':de, 'avalue':self.Auto['avalue'], 'lvl':lvl, 'branch':branch, 'dekt':dekt, 'erg':erg, 'ipErg':ipErg}
943
944
945
947 '''Provides the total recombination rate coefficient.
948
949 Calls drRate and rrRate'''
950
951 if hasattr(self, 'Temperature'):
952 temperature=self.Temperature
953 else:
954 print(' temperature is not defined')
955 self.RecombRate = {'errorMessage':' temperature is not defined'}
956 if self.Ion == 1:
957
958 self.RecombRate = {'rate':np.zeros_like(temperature), 'temperature':temperature}
959 return
960 self.rrRate()
961 self.drRate()
962 if not hasattr(self, 'DrRate'):
963 rate=self.RrRate['rate']
964 else:
965 rate=self.RrRate['rate']+self.DrRate['rate']
966 self.RecombRate={'rate':rate, 'temperature':temperature}
967 return
968
969
970
972 '''Calculates the proton density to electron density ratio.
973
974 Uses the abundance and ionization equilibrium.'''
975
976
977
978
979
980
981
982
983
984
985 if hasattr(self, 'Temperature'):
986 temperature=self.Temperature
987 else:
988 temperature = self.IoneqAll['ioneqTemperature']
989 if not hasattr(self, 'AbundanceName'):
990 self.AbundanceName = self.Defaults['abundfile']
991
992
993
994
995 nTemp=temperature.size
996
997
998 eDensity=np.zeros(nTemp,'Float64')
999 pDensity=np.zeros(nTemp,'Float64')
1000 ionDensity=0.
1001
1002
1003
1004
1005 anEl = 0
1006 ion = 1
1007 good = self.IoneqAll['ioneqAll'][anEl,ion] > 0.
1008 y2 = interpolate.splrep(np.log(self.IoneqAll['ioneqTemperature'][good]),np.log(self.IoneqAll['ioneqAll'][anEl,ion,good]),s=0)
1009 bad1 = np.log(temperature) < np.log(self.IoneqAll['ioneqTemperature'][good].min())
1010 bad2 = np.log(temperature) > np.log(self.IoneqAll['ioneqTemperature'][good].max())
1011 bad=np.logical_or(bad1,bad2)
1012 goodt=np.logical_not(bad)
1013 thisIoneq=np.where(goodt,10.**interpolate.splev(np.log(temperature),y2,der=0),0.)
1014 pDensity+=chdata.Abundance[self.AbundanceName]['abundance'][anEl]*thisIoneq
1015
1016
1017
1018 El=[iEl for iEl in range(50) if chdata.Abundance[self.AbundanceName]['abundance'][iEl] > 0.]
1019 for anEl in El:
1020 ionDensity+=chdata.Abundance[self.AbundanceName]['abundance'][anEl]
1021 for ion in range(1,anEl+2):
1022 good = self.IoneqAll['ioneqAll'][anEl,ion] > 0.
1023 y2 = interpolate.splrep(np.log(self.IoneqAll['ioneqTemperature'][good]),np.log(self.IoneqAll['ioneqAll'][anEl,ion,good]),s=0)
1024 bad1 = np.log(temperature) < np.log(self.IoneqAll['ioneqTemperature'][good].min())
1025 bad2 = np.log(temperature) > np.log(self.IoneqAll['ioneqTemperature'][good].max())
1026 bad = np.logical_or(bad1,bad2)
1027 goodt = np.logical_not(bad)
1028
1029
1030
1031
1032
1033
1034 thisIoneq=np.where(goodt,10.**interpolate.splev(np.log(temperature),y2,der=0),1.)
1035 eDensity+=float(ion)*chdata.Abundance[self.AbundanceName]['abundance'][anEl]*thisIoneq
1036 self.ProtonDensityRatio=pDensity/eDensity
1037 self.EDensity=eDensity
1038 self.IonDensity=ionDensity
1039 self.IonDensityRatio=ionDensity/eDensity
1040
1041
1042
1043
1045 """
1046 Provides the temperatures and effective collision strengths (upsilons)
1047 set prot for proton rates
1048 otherwise, ce will be set for electron collision rates
1049 uses the new format "scups" files
1050 """
1051
1052
1053
1054
1055 if prot:
1056 ce = 0
1057 try:
1058 nscups=len(self.Psplups["lvl1"])
1059 except:
1060 self.Psplups=io.splupsRead(self.IonStr,prot=1)
1061 if type(self.Psplups) == type(None):
1062 self.PUpsilon = None
1063 return
1064 else:
1065 nscups = len(self.Cilvl["lvl1"])
1066 elif diel:
1067 ce = 0
1068 try:
1069 nsplups = len(self.DielSplups["lvl1"])
1070 except:
1071 self.DielSplups = io.scupsRead(self.IonStr)
1072 if type(self.DielSplups) == type(None):
1073 self.DielUpsilon = None
1074 return
1075 else:
1076 nscups = len(self.Scups["lvl1"])
1077 else:
1078 ce=1
1079 try:
1080 nscups=len(self.Scups["lvl1"])
1081 except:
1082 self.Scups = io.scupsRead(self.IonStr)
1083 if not self.Scups['status']:
1084 self.Upsilon = None
1085 return
1086 else:
1087 nscups = len(self.Scups["lvl1"])
1088
1089
1090 if hasattr(self, 'Temperature'):
1091 temperature=self.Temperature
1092 else:
1093 print(' Temperature undefined')
1094 return {'errorMessage':' Temperature undefined'}
1095
1096
1097
1098
1099
1100 if not hasattr(self, 'Elvlc'):
1101 self.elvlcRead()
1102
1103
1104
1105
1106 eryd=np.asarray(self.Elvlc["eryd"])
1107 erydth=np.asarray(self.Elvlc["erydth"])
1108 elvlc=np.where(eryd >= 0.,eryd,erydth)
1109
1110 temp=np.asarray(temperature)
1111 ntemp=temp.size
1112 if ntemp > 1:
1113 ups = np.zeros((nscups,ntemp),"Float64")
1114 exRate = np.zeros((nscups,ntemp),"Float64")
1115 dexRate = np.zeros((nscups,ntemp),"Float64")
1116 else:
1117 ups = np.zeros(nscups,"Float64")
1118 exRate = np.zeros((nscups,ntemp),"Float64")
1119 dexRate = np.zeros((nscups,ntemp),"Float64")
1120 deAll = []
1121
1122 for iscups in range(nscups):
1123 if prot:
1124
1125 l1=self.Psplups["lvl1"][iscups]-1
1126 l1idx = self.Elvlc['lvl'].index(self.Psplups['lvl1'][iscups])
1127 l2=self.Psplups["lvl2"][iscups]-1
1128 l2idx = self.Elvlc['lvl'].index(self.Psplups['lvl2'][iscups])
1129 ttype=self.Psplups["ttype"][iscups]
1130 cups=self.Psplups["cups"][iscups]
1131 nspl=self.Psplups["nspl"][iscups]
1132 dx=1./(float(nspl)-1.)
1133 xs=dx*np.arange(nspl)
1134
1135 splups=self.Psplups["splups"][iscups]
1136 de=elvlc[l2idx]-elvlc[l1idx]
1137
1138 kte = const.boltzmann*temp/(de*const.ryd2erg)
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155 else:
1156
1157 l1=self.Scups["lvl1"][iscups]-1
1158 l1idx = self.Elvlc['lvl'].index(self.Scups['lvl1'][iscups])
1159 l2=self.Scups["lvl2"][iscups]-1
1160 l2idx = self.Elvlc['lvl'].index(self.Scups['lvl2'][iscups])
1161 ttype=self.Scups["ttype"][iscups]
1162 cups=self.Scups["cups"][iscups]
1163 nspl=self.Scups["ntemp"][iscups]
1164 xs = self.Scups['btemp'][iscups]
1165
1166 scups=self.Scups["bscups"][iscups]
1167 de=self.Scups['de'][iscups]
1168 kte = const.boltzmann*temp/(de*const.ryd2erg)
1169
1170 der=0
1171 if ttype == 1:
1172 st=1.-np.log(cups)/np.log(kte+cups)
1173 y2=interpolate.splrep(xs,scups,s=0)
1174 sups=interpolate.splev(st,y2,der=der)
1175
1176 ups[iscups]=sups*np.log(kte+np.exp(1.))
1177
1178 if ttype == 2:
1179 st=kte/(kte+cups)
1180 y2=interpolate.splrep(xs,scups,s=0)
1181 sups=interpolate.splev(st,y2,der=der)
1182 ups[iscups]=sups
1183
1184 if ttype == 3:
1185 st=kte/(kte+cups)
1186 y2=interpolate.splrep(xs,scups,s=0)
1187 sups=interpolate.splev(st,y2,der=der)
1188 ups[iscups]=sups/(kte+1.)
1189
1190 if ttype == 4:
1191 st=1.-np.log(cups)/np.log(kte+cups)
1192 y2=interpolate.splrep(xs,scups,s=0)
1193 sups=interpolate.splev(st,y2,der=der)
1194 ups[iscups]=sups*np.log(kte+cups)
1195
1196 if ttype == 5:
1197
1198 st=kte/(kte+cups)
1199
1200 y2=interpolate.splrep(xs,scups,s=0)
1201 sups=interpolate.splev(st,y2,der=der)
1202 ups[iscups]=sups/(kte+0.)
1203
1204
1205 if ttype == 6:
1206 st=kte/(kte+cups)
1207
1208 y2=interpolate.splrep(xs,scups,s=0)
1209 sups=interpolate.splev(st,y2,der=der)
1210
1211 ups[iscups]=10.**sups
1212
1213 elif ttype > 6: print(' t_type ne 1,2,3,4,5 = %5i %5i %5i '%(ttype,l1,l2))
1214
1215 if ce:
1216 if self.Dielectronic:
1217
1218 de = np.abs((elvlc[l2idx] - self.UpperIp/const.ryd2Ev) - elvlc[l1idx])
1219 else:
1220 de = np.abs(elvlc[l2idx] - elvlc[l1idx])
1221 deAll.append(de)
1222
1223 ekt = (de*const.ryd2erg)/(const.boltzmann*temp)
1224 fmult1 = float(self.Elvlc["mult"][l1idx])
1225 fmult2 = float(self.Elvlc["mult"][l2idx])
1226 dexRate[iscups] = const.collision*ups[iscups]/(fmult2*np.sqrt(temp))
1227 exRate[iscups] = const.collision*ups[iscups]*np.exp(-ekt)/(fmult1*np.sqrt(temp))
1228 elif diel:
1229
1230 de = np.abs((elvlc[l2idx] - self.Ip/const.ryd2Ev) - elvlc[l1idx])
1231 ekt = (de*const.ryd2erg)/(const.boltzmann*temp)
1232 fmult1 = float(self.Elvlc["mult"][l1idx])
1233 fmult2 = float(self.Elvlc["mult"][l2idx])
1234 exRate[iscups] = const.collision*ups[iscups]*np.exp(-ekt)/(fmult1*np.sqrt(temp))
1235 elif prot:
1236 de = np.abs(elvlc[l2idx]- elvlc[l1idx])
1237 ekt = (de*1.57888e+5)/temp
1238 fmult1 = float(self.Elvlc["mult"][l1idx])
1239 fmult2 = float(self.Elvlc["mult"][l2idx])
1240 dexRate[iscups] = const.collision*ups[iscups]/(fmult2*np.sqrt(temp))
1241 exRate[iscups] = const.collision*ups[iscups]*np.exp(-ekt)/(fmult1*np.sqrt(temp))
1242
1243 ups=np.where(ups > 0.,ups,0.)
1244
1245 if prot == 1:
1246 self.PUpsilon = {'upsilon':ups, 'temperature':temperature, 'exRate':exRate, 'dexRate':dexRate}
1247
1248
1249 else:
1250 self.Upsilon = {'upsilon':ups, 'temperature':temperature, 'exRate':exRate, 'dexRate':dexRate, 'de':deAll}
1251
1252
1253
1255 """
1256 Provides the temperatures and effective collision strengths (upsilons)
1257 set prot for proton rates
1258 otherwise, ce will be set for electron collision rates
1259 """
1260
1261
1262
1263
1264 if prot:
1265 ce = 0
1266 try:
1267 nsplups=len(self.Psplups["lvl1"])
1268 except:
1269 self.Psplups=io.splupsRead(self.IonStr,prot=1)
1270 if type(self.Psplups) == type(None):
1271 self.PUpsilon = None
1272 return
1273 else:
1274 nsplups = len(self.Cilvl["lvl1"])
1275 elif diel:
1276 ce = 0
1277 try:
1278 nsplups = len(self.DielSplups["lvl1"])
1279 except:
1280 self.DielSplups = io.splupsRead(self.IonStr,diel=1)
1281 if type(self.DielSplups) == type(None):
1282 self.DielUpsilon = None
1283 return
1284 else:
1285 nsplups = len(self.DielSplups["lvl1"])
1286 else:
1287 ce=1
1288 try:
1289 nsplups=len(self.Splups["lvl1"])
1290 except:
1291 self.Splups = io.splupsRead(self.IonStr)
1292 if type(self.Splups) == type(None):
1293 self.Upsilon = None
1294 return
1295 else:
1296 nsplups = len(self.Splups["lvl1"])
1297
1298
1299 if hasattr(self, 'Temperature'):
1300 temperature=self.Temperature
1301 else:
1302 print(' Temperature undefined')
1303 return {'errorMessage':' Temperature undefined'}
1304
1305
1306
1307
1308
1309
1310 if not hasattr(self, 'Elvlc'):
1311 self.elvlcRead()
1312
1313
1314 eryd=np.asarray(self.Elvlc["eryd"])
1315 erydth=np.asarray(self.Elvlc["erydth"])
1316 elvlc=np.where(eryd > 0.,eryd,erydth)
1317
1318 temp=np.asarray(temperature)
1319 ntemp=temp.size
1320 if ntemp > 1:
1321 ups = np.zeros((nsplups,ntemp),"Float64")
1322 exRate = np.zeros((nsplups,ntemp),"Float64")
1323 dexRate = np.zeros((nsplups,ntemp),"Float64")
1324 else:
1325 ups = np.zeros(nsplups,"Float64")
1326 exRate = np.zeros((nsplups,ntemp),"Float64")
1327 dexRate = np.zeros((nsplups,ntemp),"Float64")
1328 deAll = []
1329
1330 for isplups in range(nsplups):
1331 if prot:
1332
1333 l1=self.Psplups["lvl1"][isplups]-1
1334 l2=self.Psplups["lvl2"][isplups]-1
1335 fmult1 = self.Elvlc['mult'][l1]
1336 fmult2 = self.Elvlc['mult'][l2]
1337 ttype=self.Psplups["ttype"][isplups]
1338 cups=self.Psplups["cups"][isplups]
1339 nspl=self.Psplups["nspl"][isplups]
1340 dx=1./(float(nspl)-1.)
1341
1342 splups=self.Psplups["splups"][isplups]
1343 de=elvlc[l2]-elvlc[l1]
1344
1345 kte = const.boltzmann*temp/(de*const.ryd2erg)
1346 elif diel:
1347
1348 l1 = self.DielSplups["lvl1"][isplups]-1
1349 l2 = self.DielSplups["lvl2"][isplups]-1
1350 ttype = self.DielSplups["ttype"][isplups]
1351 cups = self.DielSplups["cups"][isplups]
1352 nspl = self.DielSplups["nspl"][isplups]
1353 ttype = self.DielSplups["ttype"][isplups]
1354 dx = 1./(float(nspl)-1.)
1355
1356 splups = self.DielSplups["splups"][isplups]
1357 de=self.DielSplups['de'][isplups]
1358 kte = const.boltzmann*temp/(de*const.ryd2erg)
1359 else:
1360
1361 l1=self.Splups["lvl1"][isplups]-1
1362 l2=self.Splups["lvl2"][isplups]-1
1363 ttype=self.Splups["ttype"][isplups]
1364 cups=self.Splups["cups"][isplups]
1365 nspl=self.Splups["nspl"][isplups]
1366 dx=1./(float(nspl)-1.)
1367
1368
1369 splups=self.Splups["splups"][isplups]
1370
1371 de=self.Splups['de'][isplups]
1372 kte = const.boltzmann*temp/(de*const.ryd2erg)
1373
1374 der=0
1375 if ttype == 1:
1376 st=1.-np.log(cups)/np.log(kte+cups)
1377 xs=dx*np.arange(nspl)
1378 y2=interpolate.splrep(xs,splups,s=0)
1379 sups=interpolate.splev(st,y2,der=der)
1380
1381 ups[isplups]=sups*np.log(kte+np.exp(1.))
1382
1383 if ttype == 2:
1384 st=kte/(kte+cups)
1385 xs=dx*np.arange(nspl)
1386 y2=interpolate.splrep(xs,splups,s=0)
1387 sups=interpolate.splev(st,y2,der=der)
1388 ups[isplups]=sups
1389
1390 if ttype == 3:
1391 st=kte/(kte+cups)
1392 xs=dx*np.arange(nspl)
1393 y2=interpolate.splrep(xs,splups,s=0)
1394 sups=interpolate.splev(st,y2,der=der)
1395 ups[isplups]=sups/(kte+1.)
1396
1397 if ttype == 4:
1398 st=1.-np.log(cups)/np.log(kte+cups)
1399 xs=dx*np.arange(nspl)
1400 y2=interpolate.splrep(xs,splups,s=0)
1401 sups=interpolate.splev(st,y2,der=der)
1402 ups[isplups]=sups*np.log(kte+cups)
1403
1404 if ttype == 5:
1405
1406 st=kte/(kte+cups)
1407 xs=dx*np.arange(nspl)
1408 y2=interpolate.splrep(xs,splups,s=0)
1409 sups=interpolate.splev(st,y2,der=der)
1410 ups[isplups]=sups/(kte+0.)
1411
1412
1413 if ttype == 6:
1414 st=kte/(kte+cups)
1415 xs=dx*np.arange(nspl)
1416 y2=interpolate.splrep(xs,splups,s=0)
1417 sups=interpolate.splev(st,y2,der=der)
1418
1419 ups[isplups]=10.**sups
1420
1421 elif ttype > 6: print(' t_type ne 1,2,3,4,5 = %5i %5i %5i'%(ttype,l1,l2))
1422
1423 if ce:
1424 if self.Dielectronic:
1425
1426 de = np.abs((elvlc[l2] - self.UpperIp/const.ryd2Ev) - elvlc[l1])
1427 else:
1428 de = np.abs(elvlc[l2] - elvlc[l1])
1429 deAll.append(de)
1430
1431 ekt = (de*const.ryd2erg)/(const.boltzmann*temp)
1432 fmult1 = float(self.Elvlc["mult"][l1])
1433 fmult2 = float(self.Elvlc["mult"][l2])
1434 dexRate[isplups] = const.collision*ups[isplups]/(fmult2*np.sqrt(temp))
1435 exRate[isplups] = const.collision*ups[isplups]*np.exp(-ekt)/(fmult1*np.sqrt(temp))
1436 elif diel:
1437
1438 de = np.abs((elvlc[l2] - self.Ip/const.ryd2Ev) - elvlc[l1])
1439 ekt = (de*const.ryd2erg)/(const.boltzmann*temp)
1440 fmult1 = float(self.Elvlc["mult"][l1])
1441 fmult2 = float(self.Elvlc["mult"][l2])
1442 exRate[isplups] = const.collision*ups[isplups]*np.exp(-ekt)/(fmult1*np.sqrt(temp))
1443 elif prot:
1444 de = np.abs(elvlc[l2]- elvlc[l1])
1445 ekt = (de*1.57888e+5)/temp
1446 fmult1 = float(self.Elvlc["mult"][l1])
1447 fmult2 = float(self.Elvlc["mult"][l2])
1448 dexRate[isplups] = ups[isplups]
1449 exRate[isplups] = (fmult1/fmult2)*ups[isplups]*np.exp(-ekt)
1450
1451 ups=np.where(ups > 0.,ups,0.)
1452
1453 if prot:
1454 self.PUpsilon = {'temperature':temperature, 'exRate':exRate, 'dexRate':dexRate}
1455 elif diel:
1456 self.DielUpsilon = {'upsilon':ups, 'temperature':temperature, 'exRate':exRate}
1457 else:
1458 self.Upsilon = {'upsilon':ups, 'temperature':temperature, 'exRate':exRate, 'dexRate':dexRate, 'de':deAll}
1459
1460
1461
1462 - def setup(self, dir=0, verbose=0):
1463 '''
1464 if ion is initiated with setup=0, this allows the setup to be done at a later point
1465 perhaps, more importantly, by setting dir to a directory cotaining the necessary files
1466 for a ChiantiPy ion, it allows one to setup an ion with files not in the current
1467 Chianti directory
1468 '''
1469
1470
1471
1472
1473 MasterList = chdata.MasterList
1474
1475 if self.IonStr in MasterList:
1476 if dir:
1477 fileName = os.path.join(dir, self.IonStr)
1478 self.Elvlc = io.elvlcRead('',filename=fileName+'.elvlc')
1479 self.Wgfa = io.wgfaRead('',filename=fileName+'.wgfa', elvlcname=fileName+'.elvlc')
1480 self.Nwgfa=len(self.Wgfa['lvl1'])
1481 nlvlWgfa = max(self.Wgfa['lvl2'])
1482 nlvlList =[nlvlWgfa]
1483
1484 scupsfile = fileName + '.scups'
1485
1486 if os.path.isfile(scupsfile):
1487
1488 self.Scups = io.scupsRead('', filename=scupsfile)
1489 self.Nscups=len(self.Scups['lvl1'])
1490 nlvlScups = max(self.Scups['lvl2'])
1491 nlvlList.append(nlvlScups)
1492 else:
1493 self.Nscups = 0
1494 nlvlScups = 0
1495 else:
1496 fileName = util.ion2filename(self.IonStr)
1497 self.Elvlc = io.elvlcRead(self.IonStr)
1498 self.Wgfa = io.wgfaRead(self.IonStr)
1499 self.Nwgfa=len(self.Wgfa['lvl1'])
1500 nlvlWgfa = max(self.Wgfa['lvl2'])
1501 nlvlList =[nlvlWgfa]
1502
1503 scupsfile = fileName + '.scups'
1504 if os.path.isfile(scupsfile):
1505
1506 self.Scups = io.scupsRead(self.IonStr)
1507 self.Nscups=len(self.Scups['lvl1'])
1508 nlvlScups = max(self.Scups['lvl2'])
1509 nlvlList.append(nlvlScups)
1510 else:
1511 self.Nscups = 0
1512 nlvlScups = 0
1513
1514
1515 file = fileName +'.cilvl'
1516 if os.path.isfile(file):
1517 self.Cilvl = io.cireclvlRead('',filename = fileName, cilvl=1)
1518 self.Ncilvl=len(self.Cilvl['lvl1'])
1519 nlvlCilvl = max(self.Cilvl['lvl2'])
1520 nlvlList.append(nlvlCilvl)
1521 else:
1522 self.Ncilvl = 0
1523
1524 reclvlfile = fileName +'.reclvl'
1525 if os.path.isfile(reclvlfile):
1526 self.Reclvl = io.cireclvlRead('',filename=fileName, reclvl=1)
1527 self.Nreclvl = len(self.Reclvl['lvl1'])
1528 nlvlReclvl = max(self.Reclvl['lvl2'])
1529 nlvlList.append(nlvlReclvl)
1530 else:
1531 self.Nreclvl = 0
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543 psplupsfile = fileName +'.psplups'
1544 if os.path.isfile(psplupsfile):
1545 self.Psplups = io.splupsRead('', filename=psplupsfile, prot=True)
1546 self.Npsplups=len(self.Psplups["lvl1"])
1547 else:
1548 self.Npsplups = 0
1549
1550 drparamsFile = fileName +'.drparams'
1551 if os.path.isfile(drparamsFile):
1552 self.DrParams = io.drRead(self.IonStr)
1553
1554 rrparamsFile = fileName +'.rrparams'
1555 if os.path.isfile(rrparamsFile):
1556 self.RrParams = io.rrRead(self.IonStr)
1557
1558
1559
1560
1561
1562
1563
1564 nlvlElvlc = len(self.Elvlc['lvl'])
1565
1566
1567
1568
1569 self.Nlvls = min([nlvlElvlc, max(nlvlList)])
1570 else:
1571 try:
1572 self.Elvlc = io.elvlcRead(self.IonStr, verbose=verbose)
1573 except:
1574 print(' the ion %s is not in the CHIANTI masterlist '%(self.IonStr))
1575 print(' elvlc file NOT available for %s'%(self.IonStr))
1576 return
1577
1578
1579
1581 '''
1582 this allows a bare-bones ion object to be setup up with just the ionization and recombination rates
1583 mainly for ions without a complete set of files - one that is not in the MasterList
1584 '''
1585
1586
1587 if dir:
1588 fileName = os.path.join(dir, self.IonStr)
1589 else:
1590 fileName = util.ion2filename(self.IonStr)
1591
1592 elvlcname=fileName+'.elvlc'
1593 if os.path.isfile(elvlcname):
1594 self.Elvlc = io.elvlcRead('',elvlcname)
1595 else:
1596 zstuff = util.convertName(self.IonStr)
1597 if zstuff['Ion'] - zstuff['Z'] != 1:
1598
1599 print(' Elvlc file missing for '+self.IonStr)
1600 return
1601
1602 file = fileName +'.cilvl'
1603 if os.path.isfile(file):
1604 self.Cilvl = io.cireclvlRead('',filename = fileName, cilvl=1)
1605 self.Ncilvl=len(self.Cilvl['lvl1'])
1606
1607
1608 else:
1609 self.Ncilvl = 0
1610
1611 reclvlfile = fileName +'.reclvl'
1612 if os.path.isfile(reclvlfile):
1613 self.Reclvl = io.cireclvlRead('',filename=fileName, reclvl=1)
1614 self.Nreclvl = len(self.Reclvl['lvl1'])
1615
1616
1617 else:
1618 self.Nreclvl = 0
1619
1620
1621 drparamsFile = fileName +'.drparams'
1622 if os.path.isfile(drparamsFile):
1623 self.DrParams = io.drRead(self.IonStr)
1624
1625 rrparamsFile = fileName +'.rrparams'
1626 if os.path.isfile(rrparamsFile):
1627 self.RrParams = io.rrRead(self.IonStr)
1628
1629
1630
1631 - def spectrum(self, wavelength, filter=(chfilters.gaussianR,1000.), label=0, allLines=1, em=0):
1632 '''
1633 Calculates the line emission spectrum for the specified ion.
1634
1635 Convolves the results of intensity to make them look like an observed spectrum
1636 the default filter is the gaussianR filter with a resolving power of 1000. Other choices
1637 include chianti.filters.box and chianti.filters.gaussian. When using the box filter,
1638 the width should equal the wavelength interval to keep the units of the continuum and line
1639 spectrum the same.
1640
1641 includes ionization equilibrium and elemental abundances
1642
1643 can be called multiple times to use different filters and widths
1644 uses label to keep the separate applications of spectrum sorted by the label
1645 for example, do .spectrum( .... labe='test1')
1646 and do .spectrum( .... label = 'test2')
1647 then will get self.Spectrum.keys() = test1, test2 and
1648 self.Spectrum['test1'] = {'intensity':aspectrum, 'wvl':wavelength, 'filter':useFilter.__name__, 'filterWidth':useFactor}
1649
1650 Note: scipy.ndimage.filters also includes a range of filters.
1651 '''
1652
1653 ylabel = r'erg cm$^{-2}$ s$^{-1}$ sr$^{-1} \AA^{-1}$ ($\int\,$ N$_e\,$N$_H\,$d${\it l}$)$^{-1}$'
1654
1655 xlabel = 'Wavelength ('+self.Defaults['wavelength'] +')'
1656
1657 nTemp = self.Temperature.size
1658 nDens = self.EDensity.size
1659 useFilter = filter[0]
1660 useFactor= filter[1]
1661
1662 wvlRange = [wavelength.min(), wavelength.max()]
1663 if hasattr(self, 'Intensity'):
1664 intensity = self.Intensity
1665 else:
1666 self.intensity(wvlRange=wvlRange, allLines=allLines, em=em)
1667 intensity = self.Intensity
1668
1669
1670 if hasattr(self, 'Em'):
1671 em = self.Em
1672 useEm = 0
1673 elif type(em) == int and em == 0:
1674 em = np.ones(self.NTempDen, 'float64')
1675 self.Em = em
1676 useEm = 1
1677 elif type(em) == float and em > 0.:
1678 em = np.ones(self.NTempDen, 'float64')*em
1679 self.Em = em
1680 useEm = 1
1681 elif type(em) == list or type(em) == tuple or type(em) == np.ndarray:
1682 em = np.asarray(em, 'float64')
1683 self.Em = em
1684 useEm = 1
1685
1686 if self.Em.any() > 0.:
1687 ylabel = r'erg cm$^{-2}$ s$^{-1}$ sr$^{-1} \AA^{-1}$ '
1688 else:
1689 ylabel = r'erg cm$^{-2}$ s$^{-1}$ sr$^{-1} \AA^{-1}$ ($\int\,$ N$_e\,$N$_H\,$d${\it l}$)$^{-1}$'
1690
1691 xlabel = 'Wavelength ('+self.Defaults['wavelength'] +')'
1692
1693 if self.NTempDen == 1:
1694 aspectrum = np.zeros_like(wavelength)
1695 if not 'errorMessage' in self.Intensity.keys():
1696 idx = util.between(self.Intensity['wvl'], wvlRange)
1697 if len(idx) == 0:
1698 print(' no lines in wavelength range %12.2f - %12.2f'%(wavelength.min(), wavelength.max()))
1699 self.Spectrum = {'errorMessage':' no lines in wavelength range %12.2f - %12.2f'%(wavelength.min(), wavelength.max())}
1700 return
1701 for iwvl in idx:
1702 wvlCalc = self.Intensity['wvl'][iwvl]
1703 aspectrum += useFilter(wavelength, wvlCalc, factor=useFactor)*intensity['intensity'][iwvl]
1704 if useEm:
1705 aspectrum *= em
1706 elif self.NTempDen > 1:
1707 aspectrum = np.zeros((self.NTempDen, wavelength.size), 'float64')
1708 if not 'errorMessage' in self.Intensity.keys():
1709 idx = util.between(self.Intensity['wvl'], wvlRange)
1710 if len(idx) == 0:
1711 print(' no lines in wavelength range %12.2f - %12.2f'%(wavelength.min(), wavelength.max()))
1712 self.Spectrum = {'errorMessage':' no lines in wavelength range %12.2f - %12.2f'%(wavelength.min(), wavelength.max())}
1713 return
1714 for itemp in range(self.NTempDen):
1715 for iwvl in idx:
1716 wvlCalc = self.Intensity['wvl'][iwvl]
1717 aspectrum[itemp] += useFilter(wavelength, wvlCalc, factor=useFactor)*self.Intensity['intensity'][itemp, iwvl]
1718 if useEm:
1719 aspectrum[itemp] *= em[itemp]
1720
1721
1722
1723 if type(label) == type(''):
1724 if hasattr(self, 'Spectrum'):
1725 self.Spectrum[label] = {'intensity':aspectrum, 'wvl':wavelength, 'filter':useFilter.__name__, 'filterWidth':useFactor, 'allLines':allLines, 'em':em, 'xlabel':xlabel, 'ylabel':ylabel}
1726 else:
1727 self.Spectrum = {label:{'intensity':aspectrum, 'wvl':wavelength, 'filter':useFilter.__name__, 'filterWidth':useFactor, 'allLines':allLines, 'em':em, 'xlabel':xlabel, 'ylabel':ylabel}}
1728
1729 else:
1730 self.Spectrum = {'intensity':aspectrum, 'wvl':wavelength, 'filter':useFilter.__name__, 'filterWidth':useFactor, 'allLines':allLines, 'em':em, 'xlabel':xlabel, 'ylabel':ylabel}
1731
1732
1733
1734 - def populate(self, popCorrect=1, verbose=0, **kwargs):
1735 """
1736 Calculate level populations for specified ion.
1737 possible keyword arguments include temperature, eDensity, pDensity, radTemperature and rStar
1738 """
1739
1740
1741 for one in kwargs.keys():
1742 if one not in chdata.keywordArgs:
1743 print(' following keyword is not understood - %20s '%(one))
1744
1745 nlvls=self.Nlvls
1746 nwgfa=self.Nwgfa
1747 nscups=self.Nscups
1748 npsplups=self.Npsplups
1749
1750 if 'temperature' in kwargs.keys():
1751 self.Temperature = np.asarray(kwargs['temperature'])
1752 temperature = self.Temperature
1753 elif hasattr(self, 'Temperature'):
1754 temperature=self.Temperature
1755 else:
1756 print(' no temperature values have been set')
1757 return
1758
1759 if 'eDensity' in kwargs.keys():
1760 self.EDensity = np.asarray(kwargs['eDensity'])
1761 eDensity = self.EDensity
1762 elif hasattr(self, 'EDensity'):
1763 eDensity = self.EDensity
1764 else:
1765 print(' no eDensity values have been set')
1766 return
1767
1768 if 'pDensity' in kwargs.keys():
1769 if kwargs['pDensity'] == 'default':
1770 self.p2eRatio()
1771 protonDensity = self.ProtonDensityRatio*self.EDensity
1772 else:
1773 try:
1774 self.PDensity = np.asarray(kwargs['pDensity'])
1775 except:
1776 print(' could not interpret value for keyword pDensity')
1777 print(' should be either "default" or a number or array')
1778 return
1779 else:
1780 if hasattr(self, 'PDensity'):
1781 protonDensity = self.PDensity
1782 else:
1783 self.p2eRatio()
1784 self.PDensity = self.ProtonDensityRatio*self.EDensity
1785 protonDensity = self.PDensity
1786 print(' proton density not specified, set to \"default\" ')
1787
1788 if 'radTemperature' in kwargs.keys() and 'rStar' in kwargs.keys():
1789 self.RadTemperature = np.asarray(kwargs['radTemperature'])
1790 radTemperature = np.array(self.RadTemperature)
1791 self.RStar = np.asarray(kwargs['rStar'])
1792 rStar = np.asarray(self.RStar)
1793 elif hasattr(self, 'RadTemperature') and hasattr(self, 'RStar'):
1794 radTemperature = self.RadTemperature
1795 rStar = self.RStar
1796
1797
1798
1799 rec = 0
1800 ci = 0
1801 if popCorrect and (not self.Dielectronic):
1802 if self.Ncilvl:
1803 ci = 1
1804 cilvl = self.Cilvl
1805 if hasattr(self, 'CilvlRate'):
1806 cilvlRate = self.CilvlRate
1807 else:
1808 self.cireclvlDescale('cilvl')
1809 cilvlRate = self.CilvlRate
1810 self.recombRate()
1811
1812 lowers = util.zion2name(self.Z, self.Ion-1)
1813
1814 lower = ion(lowers, temperature=self.Temperature, eDensity = self.EDensity)
1815 lower.ionizRate()
1816
1817 lowMult = lower.Elvlc['mult']
1818
1819
1820
1821 rec = 0
1822 if self.Nreclvl:
1823 rec = 1
1824 reclvl = self.Reclvl
1825 if hasattr(self, 'ReclvlRate'):
1826 reclvlRate = self.ReclvlRate
1827 else:
1828 self.cireclvlDescale('reclvl')
1829 reclvlRate = self.ReclvlRate
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864 if rec:
1865
1866
1867
1868
1869 self.ionizRate()
1870
1871 highers = util.zion2name(self.Z, self.Ion+1)
1872 higher = ion(highers, temperature=self.Temperature, eDensity=self.EDensity)
1873 higher.recombRate()
1874
1875
1876 rad=np.zeros((nlvls+ci+rec,nlvls+ci+rec),"float64")
1877
1878
1879 for iwgfa in range(nwgfa):
1880 l1 = self.Wgfa["lvl1"][iwgfa]-1
1881 l2 = self.Wgfa["lvl2"][iwgfa]-1
1882 rad[l1+ci,l2+ci] += self.Wgfa["avalue"][iwgfa]
1883 rad[l2+ci,l2+ci] -= self.Wgfa["avalue"][iwgfa]
1884
1885 if self.RadTemperature:
1886 if not self.RStar:
1887 dilute = 0.5
1888 else:
1889 dilute = util.dilute(self.RStar)
1890
1891 if abs(self.Wgfa['wvl'][iwgfa]) > 0.:
1892 de = const.invCm2Erg*(self.Elvlc['ecm'][l2] - self.Elvlc['ecm'][l1])
1893 dekt = de/(const.boltzmann*self.RadTemperature)
1894
1895 phexFactor = dilute*(float(self.Elvlc['mult'][l2])/float(self.Elvlc['mult'][l1]))/(np.exp(dekt) -1.)
1896 rad[l2+ci,l1+ci] += self.Wgfa["avalue"][iwgfa]*phexFactor
1897 rad[l1+ci,l1+ci] -= self.Wgfa["avalue"][iwgfa]*phexFactor
1898
1899 stemFactor = dilute/(np.exp(-dekt) -1.)
1900 rad[l1+ci,l2+ci] += self.Wgfa["avalue"][iwgfa]*stemFactor
1901 rad[l2+ci,l2+ci] -= self.Wgfa["avalue"][iwgfa]*stemFactor
1902
1903
1904
1905 if self.Nscups:
1906 self.upsilonDescale(diel=self.Dielectronic)
1907
1908 exRate = self.Upsilon['exRate']
1909 dexRate = self.Upsilon['dexRate']
1910
1911 if npsplups:
1912 self.upsilonDescaleSplups(prot=1)
1913
1914 pexRate = self.PUpsilon['exRate']
1915 pdexRate = self.PUpsilon['dexRate']
1916
1917
1918 temp = temperature
1919 ntemp = temp.size
1920 dens = self.EDensity
1921 ndens = dens.size
1922 cc=const.collision*self.EDensity
1923
1924 if npsplups:
1925 cp=const.collision*protonDensity
1926 if ntemp > 1 and ndens >1 and ntemp != ndens:
1927 print(' unless temperature or eDensity are single values')
1928 print(' the number of temperatures values must match the ')
1929 print(' the number of eDensity values')
1930 return
1931
1932
1933
1934 nscups = self.Nscups
1935
1936
1937
1938
1939
1940
1941 if ndens == 1 and ntemp == 1:
1942 popmat=np.copy(rad)
1943 if not self.Dielectronic:
1944 for iscups in range(0,nscups):
1945 l1=self.Scups["lvl1"][iscups]-1
1946 l2=self.Scups["lvl2"][iscups]-1
1947
1948 popmat[l1+ci,l2+ci] += self.EDensity*dexRate[iscups]
1949 popmat[l2+ci,l1+ci] += self.EDensity*exRate[iscups]
1950 popmat[l1+ci,l1+ci] -= self.EDensity*exRate[iscups]
1951 popmat[l2+ci,l2+ci] -= self.EDensity*dexRate[iscups]
1952
1953 for isplups in range(0,npsplups):
1954 l1=self.Psplups["lvl1"][isplups]-1
1955 l2=self.Psplups["lvl2"][isplups]-1
1956
1957 popmat[l1+ci,l2+ci] += self.PDensity*pdexRate[isplups]
1958 popmat[l2+ci,l1+ci] += self.PDensity*pexRate[isplups]
1959 popmat[l1+ci,l1+ci] -= self.PDensity*pexRate[isplups]
1960 popmat[l2+ci,l2+ci] -= self.PDensity*pdexRate[isplups]
1961
1962 if ci:
1963
1964
1965
1966
1967 ciTot = 0.
1968 for itrans in range(len(cilvl['lvl1'])):
1969 lvl1 = cilvl['lvl1'][itrans]-1
1970 lvl2 = cilvl['lvl2'][itrans]-1
1971
1972
1973
1974
1975 popmat[lvl2+ci, lvl1] += self.EDensity*self.CilvlRate['rate'][itrans]
1976 popmat[lvl1, lvl1] -= self.EDensity*self.CilvlRate['rate'][itrans]
1977 ciTot += self.EDensity*self.CilvlRate['rate'][itrans]
1978
1979 popmat[1, 0] += (self.EDensity*lower.IonizRate['rate'] - ciTot)
1980 popmat[0, 0] -= (self.EDensity*lower.IonizRate['rate'] - ciTot)
1981 popmat[0, 1] += self.EDensity*self.RecombRate['rate']
1982 popmat[1, 1] -= self.EDensity*self.RecombRate['rate']
1983 if rec:
1984
1985
1986
1987 for itrans in range(self.Nreclvl):
1988
1989 lvl2 = reclvl['lvl2'][itrans]-1
1990 popmat[lvl2+ci, -1] += self.EDensity*reclvlRate['rate'][itrans]
1991 popmat[-1, -1] -= self.EDensity*reclvlRate['rate'][itrans]
1992 if self.Nreclvl:
1993 recTot = reclvlRate['rate'].sum(axis=0)
1994 else:
1995 recTot = 0.
1996
1997
1998 popmat[-1, ci] += self.EDensity*self.IonizRate['rate']
1999 popmat[ci, ci] -= self.EDensity*self.IonizRate['rate']
2000
2001 popmat[ci, -1] += self.EDensity*(higher.RecombRate['rate']- recTot)
2002 popmat[-1, -1] -= self.EDensity*(higher.RecombRate['rate']- recTot)
2003
2004 if self.Dielectronic:
2005
2006 for iscups in range(0,nscups):
2007 l1=self.Scups["lvl1"][iscups]-1
2008 l2=self.Scups["lvl2"][iscups]-1
2009
2010 popmat[l2+ci,-1] += self.EDensity*exRate[iscups]
2011 popmat[-1, -1] -= self.EDensity*exRate[iscups]
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042 norm=np.ones(nlvls+ci+rec,'float64')
2043 if ci:
2044 norm[0] = 0.
2045 if rec:
2046 norm[nlvls+ci+rec-1] = 0.
2047 if self.Dielectronic:
2048 norm[nlvls-1] = 0.
2049 popmat[nlvls+ci+rec-1]=norm
2050
2051
2052
2053 b=np.zeros(nlvls+ci+rec,'float64')
2054 b[nlvls+ci+rec-1]=1.
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068 try:
2069 fullpop=np.linalg.solve(popmat,b)
2070 pop = fullpop[ci:ci+nlvls]
2071 except np.linalg.LinAlgError:
2072 pop = np.zeros(nlvls, 'float64')
2073
2074
2075
2076
2077
2078 elif ndens == 1:
2079
2080 pop=np.zeros((ntemp, nlvls),"float64")
2081
2082 for itemp in range(0,ntemp):
2083 popmat=np.copy(rad)
2084 for iscups in range(0,nscups):
2085 l1=self.Scups["lvl1"][iscups]-1
2086 l2=self.Scups["lvl2"][iscups]-1
2087 popmat[l1+ci,l2+ci] += self.EDensity*dexRate[iscups, itemp]
2088 popmat[l2+ci,l1+ci] += self.EDensity*exRate[iscups, itemp]
2089 popmat[l1+ci,l1+ci] -= self.EDensity*exRate[iscups, itemp]
2090 popmat[l2+ci,l2+ci] -= self.EDensity*dexRate[iscups, itemp]
2091 for isplups in range(0,npsplups):
2092 l1=self.Psplups["lvl1"][isplups]-1
2093 l2=self.Psplups["lvl2"][isplups]-1
2094
2095
2096 popmat[l1+ci,l2+ci] += self.PDensity[itemp]*pdexRate[isplups, itemp]
2097 popmat[l2+ci,l1+ci] += self.PDensity[itemp]*pexRate[isplups, itemp]
2098 popmat[l1+ci,l1+ci] -= self.PDensity[itemp]*pexRate[isplups, itemp]
2099 popmat[l2+ci,l2+ci] -= self.PDensity[itemp]*pdexRate[isplups, itemp]
2100
2101 if ci:
2102
2103
2104
2105
2106 ciTot = 0.
2107 for itrans in range(len(cilvl['lvl1'])):
2108 lvl1 = cilvl['lvl1'][itrans]-1
2109 lvl2 = cilvl['lvl2'][itrans]-1
2110
2111
2112
2113
2114 popmat[lvl2+ci, lvl1] += self.EDensity*self.CilvlRate['rate'][itrans, itemp]
2115 popmat[lvl1, lvl1] -= self.EDensity*self.CilvlRate['rate'][itrans, itemp]
2116 ciTot += self.EDensity*self.CilvlRate['rate'][itrans, itemp]
2117
2118
2119 popmat[1, 0] += (self.EDensity*lower.IonizRate['rate'][itemp] - ciTot)
2120 popmat[0, 0] -= (self.EDensity*lower.IonizRate['rate'][itemp] - ciTot)
2121 popmat[0, 1] += self.EDensity*self.RecombRate['rate'][itemp]
2122 popmat[1, 1] -= self.EDensity*self.RecombRate['rate'][itemp]
2123 if rec:
2124
2125 if self.Nreclvl:
2126 recTot = self.ReclvlRate['rate'][:, itemp].sum()
2127 else:
2128 recTot = 0.
2129
2130 popmat[-1, ci] += self.EDensity*self.IonizRate['rate'][itemp]
2131 popmat[ci, ci] -= self.EDensity*self.IonizRate['rate'][itemp]
2132 popmat[ci, -1] += self.EDensity*(higher.RecombRate['rate'][itemp]- recTot)
2133 popmat[-1, -1] -= self.EDensity*(higher.RecombRate['rate'][itemp]- recTot)
2134
2135
2136
2137
2138
2139
2140 for itrans in range(self.Nreclvl):
2141 lvl1 = reclvl['lvl1'][itrans]-1
2142 lvl2 = reclvl['lvl2'][itrans]-1
2143 popmat[lvl2+ci, -1] += self.EDensity*self.ReclvlRate['rate'][itrans, itemp]
2144 popmat[-1, -1] -= self.EDensity*self.ReclvlRate['rate'][itrans, itemp]
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172 norm=np.ones(nlvls+ci+rec,'float64')
2173 if ci:
2174 norm[0] = 0.
2175 if rec:
2176 norm[-1] = 0.
2177 if self.Dielectronic:
2178 norm[-1] = 0.
2179 popmat[nlvls+ci+rec-1]=norm
2180 b=np.zeros(nlvls+ci+rec,'float64')
2181 b[nlvls+ci+rec-1]=1.
2182 try:
2183 thispop=np.linalg.solve(popmat,b)
2184 pop[itemp] = thispop[ci:ci+nlvls]
2185 except np.linalg.LinAlgError:
2186 pop[itemp] = np.zeros(nlvls, 'float64')
2187
2188
2189 elif ntemp == 1:
2190
2191
2192 pop=np.zeros((ndens,nlvls),"float64")
2193 for idens in range(0,ndens):
2194 popmat=np.copy(rad)
2195 for isplups in range(0,nscups):
2196 l1=self.Scups["lvl1"][isplups]-1
2197 l2=self.Scups["lvl2"][isplups]-1
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210 popmat[l1+ci,l2+ci] += self.EDensity[idens]*dexRate[isplups]
2211 popmat[l2+ci,l1+ci] += self.EDensity[idens]*exRate[isplups]
2212 popmat[l1+ci,l1+ci] -= self.EDensity[idens]*exRate[isplups]
2213 popmat[l2+ci,l2+ci] -= self.EDensity[idens]*dexRate[isplups]
2214
2215 for isplups in range(0,npsplups):
2216 l1=self.Psplups["lvl1"][isplups]-1
2217 l2=self.Psplups["lvl2"][isplups]-1
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228 popmat[l1+ci,l2+ci] += self.PDensity[idens]*pdexRate[isplups]
2229 popmat[l2+ci,l1+ci] += self.PDensity[idens]*pexRate[isplups]
2230 popmat[l1+ci,l1+ci] -= self.PDensity[idens]*pexRate[isplups]
2231 popmat[l2+ci,l2+ci] -= self.PDensity[idens]*pdexRate[isplups]
2232
2233 if ci:
2234
2235
2236
2237 ciTot = 0.
2238 for itrans in range(len(cilvl['lvl1'])):
2239 lvl1 = cilvl['lvl1'][itrans] -1
2240 lvl2 = cilvl['lvl2'][itrans] -1
2241
2242
2243
2244
2245
2246 popmat[lvl2+ci, lvl1] += self.EDensity[idens]*self.CilvlRate['rate'][itrans]
2247 popmat[lvl1, lvl1] -= self.EDensity[idens]*self.CilvlRate['rate'][itrans]
2248 ciTot += self.EDensity[idens]*self.CilvlRate['rate'][itrans]
2249 popmat[1, 0] += (self.EDensity[idens]*lower.IonizRate['rate'] -ciTot)
2250 popmat[0, 0] -= (self.EDensity[idens]*lower.IonizRate['rate'] -ciTot)
2251 popmat[0, 1] += self.EDensity[idens]*self.RecombRate['rate']
2252 popmat[1, 1] -= self.EDensity[idens]*self.RecombRate['rate']
2253 if rec:
2254
2255 if self.Nreclvl:
2256
2257 recTot = self.ReclvlRate['rate'].sum()
2258 else:
2259 recTot = 0.
2260
2261 popmat[-1, ci] += self.EDensity[idens]*self.IonizRate['rate']
2262 popmat[ci, ci] -= self.EDensity[idens]*self.IonizRate['rate']
2263 popmat[ci, -1] += self.EDensity[idens]*(higher.RecombRate['rate'] - recTot)
2264 popmat[-1, -1] -= self.EDensity[idens]*(higher.RecombRate['rate'] - recTot)
2265
2266
2267
2268
2269 for itrans in range(self.Nreclvl):
2270 lvl1 = reclvl['lvl1'][itrans]-1
2271 lvl2 = reclvl['lvl2'][itrans]-1
2272 popmat[lvl2+ci, -1] += self.EDensity[idens]*self.ReclvlRate['rate'][itrans]
2273 popmat[-1, -1] -= self.EDensity[idens]*self.ReclvlRate['rate'][itrans]
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302 norm=np.ones(nlvls+ci+rec,'float64')
2303 if ci:
2304 norm[0] = 0.
2305 if rec:
2306 norm[-1] = 0.
2307 if self.Dielectronic:
2308 norm[-1] = 0.
2309 popmat[nlvls+ci+rec-1]=norm
2310 b=np.zeros(nlvls+ci+rec,'float64')
2311 b[nlvls+ci+rec-1]=1.
2312 try:
2313 thispop=np.linalg.solve(popmat,b)
2314 pop[idens] = thispop[ci:ci+nlvls]
2315 except np.linalg.LinAlgError:
2316 pop[idens] = np.zeros(nlvls, 'float64')
2317
2318
2319
2320
2321
2322
2323
2324
2325 elif ntemp>1 and ntemp==ndens:
2326
2327 pop=np.zeros((ntemp,nlvls),"float64")
2328
2329 for itemp in range(0,ntemp):
2330 temp=self.Temperature[itemp]
2331 popmat=np.copy(rad)
2332 for isplups in range(0,nscups):
2333 l1=self.Scups["lvl1"][isplups]-1
2334 l2=self.Scups["lvl2"][isplups]-1
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347 popmat[l1+ci,l2+ci] += self.EDensity[itemp]*dexRate[isplups, itemp]
2348 popmat[l2+ci,l1+ci] += self.EDensity[itemp]*exRate[isplups, itemp]
2349 popmat[l1+ci,l1+ci] -= self.EDensity[itemp]*exRate[isplups, itemp]
2350 popmat[l2+ci,l2+ci] -= self.EDensity[itemp]*dexRate[isplups, itemp]
2351
2352 for isplups in range(0,npsplups):
2353 l1=self.Psplups["lvl1"][isplups]-1
2354 l2=self.Psplups["lvl2"][isplups]-1
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365 popmat[l1+ci,l2+ci] += self.PDensity[itemp]*pdexRate[isplups, itemp]
2366 popmat[l2+ci,l1+ci] += self.PDensity[itemp]*pexRate[isplups, itemp]
2367 popmat[l1+ci,l1+ci] -= self.PDensity[itemp]*pexRate[isplups, itemp]
2368 popmat[l2+ci,l2+ci] -= self.PDensity[itemp]*pdexRate[isplups, itemp]
2369
2370 if ci:
2371
2372
2373
2374
2375 ciTot = 0.
2376 for itrans in range(len(cilvl['lvl1'])):
2377 lvl1 = cilvl['lvl1'][itrans] -1
2378 lvl2 = cilvl['lvl2'][itrans] -1
2379
2380
2381
2382 popmat[lvl2+ci, lvl1] += self.EDensity[itemp]*self.CilvlRate['rate'][itrans, itemp]
2383 popmat[lvl1, lvl1] -= self.EDensity[itemp]*self.CilvlRate['rate'][itrans, itemp]
2384 ciTot += self.EDensity[itemp]*self.CilvlRate['rate'][itrans, itemp]
2385 popmat[1, 0] += (self.EDensity[itemp]*lower.IonizRate['rate'][itemp] - ciTot)
2386 popmat[0, 0] -= (self.EDensity[itemp]*lower.IonizRate['rate'][itemp] - ciTot)
2387 popmat[0, 1] += self.EDensity[itemp]*self.RecombRate['rate'][itemp]
2388 popmat[1, 1] -= self.EDensity[itemp]*self.RecombRate['rate'][itemp]
2389 if rec:
2390
2391 if self.Nreclvl:
2392 recTot = self.ReclvlRate['rate'][:, itemp].sum()
2393 else:
2394 recTot = 0.
2395
2396
2397 popmat[-1, ci] += self.EDensity[itemp]*self.IonizRate['rate'][itemp]
2398 popmat[ci, ci] -= self.EDensity[itemp]*self.IonizRate['rate'][itemp]
2399 popmat[ci, -1] += self.EDensity[itemp]*(higher.RecombRate['rate'][itemp] - recTot)
2400 popmat[-1, -1] -= self.EDensity[itemp]*(higher.RecombRate['rate'][itemp] - recTot)
2401
2402
2403
2404 for itrans in range(self.Nreclvl):
2405 lvl1 = reclvl['lvl1'][itrans]-1
2406 lvl2 = reclvl['lvl2'][itrans]-1
2407 popmat[lvl2+ci, -1] += self.EDensity[itemp]*self.ReclvlRate['rate'][itrans, itemp]
2408 popmat[-1, -1] -= self.EDensity[itemp]*self.ReclvlRate['rate'][itrans, itemp]
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435 norm=np.ones(nlvls+ci+rec,'float64')
2436 if ci:
2437 norm[0] = 0.
2438 if rec:
2439 norm[-1] = 0.
2440 if self.Dielectronic:
2441 norm[-1] = 0.
2442 popmat[nlvls+ci+rec-1]=norm
2443 b=np.zeros(nlvls+ci+rec,'float64')
2444 b[nlvls+ci+rec-1]=1.
2445 try:
2446 thispop=np.linalg.solve(popmat,b)
2447 pop[itemp] = thispop[ci:ci+nlvls]
2448 except np.linalg.LinAlgError:
2449 pop[itemp] = np.zeros(nlvls, 'float64')
2450
2451
2452
2453
2454
2455
2456
2457
2458 pop=np.where(pop >0., pop,0.)
2459 self.Population={"temperature":temperature,"eDensity":eDensity,"population":pop, "protonDensity":protonDensity, "ci":ci, "rec":rec}
2460
2461 return
2462
2463
2464
2465 - def popPlot(self,top=10, plotFile=0, outFile=0, pub=0):
2466 """
2467 Plots populations vs temperature or eDensity.
2468
2469 top specifies the number of the most highly populated levels to plot
2470 if pub is set, the want publication plots (bw, lw=2).
2471 """
2472
2473 if pub:
2474 fontsize=16
2475 else:
2476 fontsize=14
2477
2478 if hasattr(self, 'Population'):
2479 temperature=self.Population["temperature"]
2480 eDensity=self.Population["eDensity"]
2481 pop=self.Population["population"]
2482 else:
2483 self.populate()
2484 temperature=self.Population["temperature"]
2485 eDensity=self.Population["eDensity"]
2486 pop=self.Population["population"]
2487
2488
2489 if len(pop.shape) == 1:
2490 spop = np.sort(pop)
2491 idx = np.argsort(pop)
2492 minPop = spop[-top:].min()/2.
2493 if top > pop.size:
2494 top = pop.size
2495 for itop in range(1, top+1):
2496 x = [idx[-itop], idx[-itop], idx[-itop]+1, idx[-itop]+1]
2497 y = [minPop, spop[-itop], spop[-itop], minPop]
2498 pl.semilogy(x, y, 'k')
2499 pl.axis([0, max(idx[-top:])+1, minPop, 1.])
2500 pl.xlabel('Level', fontsize=fontsize)
2501 pl.ylabel('Population', fontsize=fontsize)
2502 return
2503
2504
2505
2506 lvl=self.Elvlc["lvl"]
2507
2508 nlvls = self.Nlvls
2509 if top > nlvls:
2510 top = nlvls
2511 maxpop=np.zeros(nlvls,'Float64')
2512 for ilvl in range(nlvls):
2513 maxpop[ilvl]=pop[:,ilvl].max()
2514
2515 lvlsort=np.take(lvl,np.argsort(maxpop))
2516 toplvl=lvlsort[-top:]
2517
2518
2519 ntemp = temperature.size
2520 if ntemp > 0:
2521
2522 if temperature[0] == temperature[-1]:
2523 ntemp = 1
2524
2525 ndens = eDensity.size
2526 if ndens > 0:
2527
2528 if eDensity[0] == eDensity[-1]:
2529 ndens = 1
2530
2531
2532 ylabel='Population'
2533 title=self.Spectroscopic
2534
2535 pl.figure()
2536
2537 pl.ion()
2538
2539
2540 if ndens == 1:
2541 toppops = np.zeros((top, ntemp), 'float64')
2542 for ilvl in range(top):
2543 toppops[ilvl] = pop[:, toplvl[ilvl]-1]
2544 nonzero = toppops > 0.
2545 ymin = min(toppops[nonzero])
2546 for lvl in toplvl:
2547
2548 good = pop[:, lvl-1] > 0
2549 if pub:
2550 pl.loglog(temperature[good],pop[good,lvl-1], 'k',lw=2)
2551 else:
2552 pl.loglog(temperature[good],pop[good,lvl-1])
2553 skip=3
2554 if good.sum() == ntemp:
2555 start=divmod(lvl,ntemp)[1]
2556 for itemp in range(start,ntemp,ntemp//skip):
2557 pl.text(temperature[itemp],pop[itemp,lvl-1],str(lvl))
2558 else:
2559 newtemp=[]
2560 for i, one in enumerate(temperature):
2561 if good[i]:
2562 newtemp.append(one)
2563 start = divmod(lvl, len(newtemp))[1] + ntemp - good.sum()
2564 for itemp in range(start,ntemp,ntemp//skip):
2565 pl.text(temperature[itemp],pop[itemp,lvl-1],str(lvl))
2566 xlabel='Temperature (K)'
2567 pl.xlabel(xlabel,fontsize=fontsize)
2568 pl.ylabel(ylabel,fontsize=fontsize)
2569 dstr=' - Density = %10.2e (cm$^{-3}$)' % eDensity[0]
2570 pl.title(title+dstr,fontsize=fontsize)
2571 pl.xlim(temperature.min(),temperature.max())
2572
2573
2574 pl.ylim(ymin,1.2)
2575 elif ntemp == 1:
2576 xlabel=r'Electron Density (cm$^{-3}$)'
2577
2578
2579
2580
2581
2582
2583 toppops = np.zeros((top, ndens), 'float64')
2584 for ilvl in range(top):
2585 toppops[ilvl] = pop[:, toplvl[ilvl]-1]
2586 nonzero = toppops > 0.
2587 ymin = min(toppops[nonzero])
2588 for lvl in toplvl:
2589
2590 good = pop[:, lvl-1] > 0
2591 if pub:
2592 pl.loglog(eDensity[good],pop[good,lvl-1], 'k', lw=2)
2593 else:
2594 pl.loglog(eDensity[good],pop[good,lvl-1])
2595 skip=3
2596 if good.sum() == ndens:
2597 start=divmod(lvl,ndens)[1]
2598 for idens in range(start,ndens,ndens//skip):
2599 pl.text(eDensity[idens],pop[idens,lvl-1],str(lvl))
2600 else:
2601 newdens=[]
2602 for i, one in enumerate(eDensity):
2603 if good[i]:
2604 newdens.append(one)
2605 start = divmod(lvl, len(newdens))[1] + ndens - good.sum()
2606 for idens in range(start,ndens,ndens//skip):
2607 pl.text(eDensity[idens],pop[idens, lvl-1],str(lvl))
2608 pl.xlabel(xlabel,fontsize=fontsize)
2609 pl.ylabel(ylabel,fontsize=fontsize)
2610 tstr=' - T = %10.2e (K)' % temperature[0]
2611 pl.title(title+tstr,fontsize=fontsize)
2612 pl.xlim(eDensity[eDensity.nonzero()].min(),eDensity.max())
2613 yl=pl.ylim()
2614 pl.ylim(yl[0],1.2)
2615 else:
2616
2617 ax = pl.subplot(111)
2618
2619
2620
2621
2622
2623
2624 toppops = np.zeros((top, ntemp), 'float64')
2625 for ilvl in range(top):
2626 toppops[ilvl] = pop[:, toplvl[ilvl]-1]
2627 nonzero = toppops > 0.
2628 ymin = min(toppops[nonzero])
2629 for lvl in toplvl:
2630
2631 good = pop[:, lvl-1] > 0
2632 if pub:
2633 pl.loglog(temperature[good],pop[good,lvl-1], 'k', lw=2)
2634 else:
2635 pl.loglog(temperature[good],pop[good,lvl-1])
2636 skip=3
2637 if good.sum() == ntemp:
2638 start=divmod(lvl,ntemp)[1]
2639 for itemp in range(start,ntemp,ntemp//skip):
2640 pl.text(temperature[itemp],pop[itemp,lvl-1],str(lvl))
2641 else:
2642 newtemp=[]
2643 for i, one in enumerate(temperature):
2644 if good[i]:
2645 newtemp.append(one)
2646 start = divmod(lvl, len(newtemp))[1] + ntemp - good.sum()
2647 for itemp in range(start,ntemp,ntemp//skip):
2648 pl.text(temperature[itemp],pop[itemp,lvl-1],str(lvl))
2649 xlabel='Temperature (K)'
2650 pl.xlabel(xlabel,fontsize=fontsize)
2651 pl.ylabel(ylabel,fontsize=fontsize)
2652
2653
2654
2655
2656 pl.axis([temperature.min(),temperature.max(), ymin, 1.2])
2657 pl.text(0.1, 0.5,title, horizontalalignment='center', verticalalignment='center', fontsize=fontsize, transform = ax.transAxes)
2658
2659 ax2 = pl.twiny()
2660 xlabel=r'Electron Density (cm$^{-3}$)'
2661 pl.xlabel(xlabel, fontsize=fontsize)
2662 pl.loglog(eDensity,pop[:,toplvl[0]], visible=False)
2663 ax2.xaxis.tick_top()
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678 if outFile:
2679 pl.savefig(outFile)
2680 self.Population['toplvl'] = toplvl
2681 return
2682
2683
2684
2685 - def emiss(self, wvlRange = 0, allLines=1):
2686 """
2687 Calculate the emissivities for lines of the specified ion.
2688
2689 wvlRange can be set to limit the calculation to a particular wavelength range
2690
2691 units: ergs cm^-3 s^-1 str^-1
2692
2693 Does not include elemental abundance or ionization fraction
2694
2695 Wavelengths are sorted
2696 set allLines = 1 to include unidentified lines
2697 """
2698
2699
2700 if hasattr(self, 'Population'):
2701
2702 pop=self.Population['population']
2703 else:
2704 self.populate()
2705 pop=self.Population["population"]
2706
2707
2708
2709
2710
2711 wvl = np.asarray(self.Wgfa["wvl"], 'float64')
2712 obs = np.where(wvl > 0., 'Y', 'N')
2713 if allLines:
2714 wvl=np.abs(wvl)
2715 l1 = np.asarray(self.Wgfa['lvl1'], 'int64')
2716 l2 = np.asarray(self.Wgfa["lvl2"], 'int64')
2717 avalue = np.asarray(self.Wgfa["avalue"], 'float64')
2718 if 'pretty1' in self.Wgfa.keys():
2719 pretty1 = np.asarray(self.Wgfa['pretty1'])
2720 if 'pretty2' in self.Wgfa.keys():
2721 pretty2 = np.asarray(self.Wgfa['pretty2'])
2722
2723
2724
2725 if wvlRange:
2726 realgood = util.between(wvl, wvlRange)
2727 l1 = l1[realgood]
2728 l2 = l2[realgood]
2729 wvl = wvl[realgood]
2730 avalue = avalue[realgood]
2731 obs = obs[realgood]
2732 if 'pretty1' in self.Wgfa.keys():
2733 pretty1 = pretty1[realgood]
2734 if 'pretty2' in self.Wgfa.keys():
2735 pretty2 = pretty2[realgood]
2736
2737
2738
2739 nonzed = wvl != 0.
2740 wvl = wvl[nonzed]
2741 l1 = l1[nonzed]
2742 l2 = l2[nonzed]
2743 avalue = avalue[nonzed]
2744 pretty1 = pretty1[nonzed]
2745 pretty2 = pretty2[nonzed]
2746 obs = obs[nonzed]
2747 nwvl=len(wvl)
2748
2749 if nwvl == 0:
2750 self.Emiss = {'errorMessage':self.Spectroscopic+' no lines in this wavelength range'}
2751 return
2752
2753
2754 try:
2755 ntempden,nlvls = pop.shape
2756 em=np.zeros((nwvl, ntempden),'Float64')
2757
2758
2759
2760
2761 if self.EDensity.size < ntempden:
2762 eDensity = np.repeat(self.EDensity, ntempden)
2763 else:
2764 eDensity = self.EDensity
2765 except:
2766
2767 ntempden=1
2768 em=np.zeros(nwvl,'Float64')
2769 eDensity = self.EDensity
2770
2771
2772 plotLabels={}
2773
2774 if self.Defaults['wavelength'] == 'angstrom':
2775 plotLabels["xLabel"]="Angstroms"
2776 elif self.Defaults['wavelength'] == 'nm':
2777 plotLabels["xLabel"]="nanometers"
2778 elif self.Defaults['wavelength'] == 'kev':
2779 plotLabels["xLabel"] = "kev"
2780
2781 if self.Defaults['flux'] == 'energy':
2782 factor=const.planck*const.light/(4.*const.pi*1.e-8*wvl)
2783 plotLabels["yLabel"]="ergs cm^-3 s^-1"
2784 elif self.Defaults['flux'] == 'photon':
2785 factor=np.ones((nwvl),'Float64')/(4.*const.pi)
2786 plotLabels["yLabel"]="photons cm^-3 s^-1"
2787
2788 if ntempden > 1:
2789 for itempden in range(ntempden):
2790 for iwvl in range(nwvl):
2791 p = pop[itempden,l2[iwvl]-1]
2792
2793 em[iwvl, itempden] = factor[iwvl]*p*avalue[iwvl]
2794 if self.Defaults['wavelength'] == 'kev':
2795 wvl = const.ev2Ang/np.asarray(wvl)
2796 elif self.Defaults['wavelength'] == 'nm':
2797 wvl = wvl/10.
2798
2799
2800
2801
2802
2803 else:
2804 for iwvl in range(0,nwvl):
2805 p=pop[l2[iwvl]-1]
2806 em[iwvl]=factor[iwvl]*p*avalue[iwvl]
2807 if self.Defaults['wavelength'] == 'kev':
2808 wvlE=const.ev2Ang/np.asarray(wvl)
2809 elif self.Defaults['wavelength'] == 'nm':
2810 wvl=wvl/10.
2811
2812
2813
2814
2815
2816
2817
2818 nlvl = len(l1)
2819 ionS = np.asarray([self.IonStr]*nlvl)
2820 Emiss = {'ionS':ionS,"wvl":wvl, "emiss":em, "plotLabels":plotLabels, 'lvl1':l1, 'lvl2':l2, 'avalue':avalue, 'obs':obs, 'pretty1':pretty1, 'pretty2':pretty2}
2821
2822
2823
2824
2825
2826 self.Emiss = Emiss
2827 return
2828
2829
2830
2831 - def emissList(self, index=-1, wvlRange=None, wvlRanges=None, top=10, relative=0, outFile=0 ):
2832 '''
2833 List the emissivities.
2834
2835 wvlRange, a 2 element tuple, list or array determines the wavelength range
2836
2837 Top specifies to plot only the top strongest lines, default = 10
2838
2839 normalize = 1 specifies whether to normalize to strongest line, default = 0
2840 '''
2841
2842 if outFile:
2843 output = open(outFile, 'w')
2844
2845 if not hasattr(self, 'Emiss'):
2846 try:
2847 self.emiss()
2848 except:
2849 print(' emissivities not calculated or emiss() is unable to calculate them')
2850 print(' perhaps the temperature and/or eDensity are not set')
2851 return
2852
2853 emissivity = copy.copy(self.Emiss)
2854 emiss = emissivity['emiss']
2855 ionS = emissivity['ionS']
2856 wvl = emissivity['wvl']
2857 lvl1 = emissivity['lvl1']
2858 lvl2 = emissivity['lvl2']
2859 avalue = emissivity['avalue']
2860 obs = emissivity['obs']
2861 pretty1 = emissivity['pretty1']
2862 pretty2 = emissivity['pretty2']
2863
2864
2865 temperature = self.Temperature
2866 eDensity = self.EDensity
2867
2868 ndens = eDensity.size
2869 ntemp = temperature.size
2870
2871 if ndens == 1 and ntemp == 1:
2872 dstr = ' - Density = %10.2e (cm$^{-3}$)' %(eDensity)
2873 tstr = ' - T = %10.2e (K)' %(temperature)
2874 elif ndens == 1 and ntemp > 1:
2875 if index < 0:
2876 index = ntemp/2
2877 print('using index = %5i specifying temperature = %10.2e'%(index, temperature[index]))
2878 self.Message = 'using index = %5i specifying temperature = %10.2e'%(index, temperature[index])
2879
2880 emiss=emiss[:, index]
2881 elif ndens > 1 and ntemp == 1:
2882 if index < 0:
2883 index = ntemp/2
2884 print('using index =%5i specifying eDensity = %10.2e'%(index, eDensity[index]))
2885 self.Message = 'using index =%5i specifying eDensity = %10.2e'%(index, eDensity[index])
2886 emiss=emiss[:, index]
2887 elif ndens > 1 and ntemp > 1:
2888 if index < 0:
2889 index = ntemp/2
2890 print('using index = %5i specifying temperature = %10.2e, eDensity = %10.2e'%(index, temperature[index], eDensity[index]))
2891 self.Message = 'using index = %5i specifying temperature = %10.2e, eDensity = %10.2e'%(index, temperature[index], eDensity[index])
2892 emiss=emiss[:, index]
2893
2894 if wvlRange:
2895 wvlIndex=util.between(wvl,wvlRange)
2896 elif wvlRanges:
2897 wvlIndex = []
2898 for awvlRange in wvlRanges:
2899 wvlIndex.extend(util.between(wvl,awvlRange))
2900 else:
2901 wvlIndex = range(wvl.size)
2902
2903
2904
2905
2906 emiss = emiss[wvlIndex]
2907 ionS = ionS[wvlIndex]
2908 wvl = wvl[wvlIndex]
2909 lvl1 = lvl1[wvlIndex]
2910 lvl2 = lvl2[wvlIndex]
2911 avalue = avalue[wvlIndex]
2912 obs = obs[wvlIndex]
2913 pretty1 = pretty1[wvlIndex]
2914 pretty2 = pretty2[wvlIndex]
2915
2916 self.Error = 0
2917 if wvl.size == 0:
2918 print('No lines in this wavelength interval')
2919 self.Error = 1
2920 self.Message = 'No lines in this wavelength interval'
2921 return
2922
2923 elif top == 0:
2924 top = wvl.size
2925 elif top > wvl.size:
2926 top = wvl.size
2927
2928
2929 isrt = np.argsort(emiss)
2930 ionS = ionS[isrt[-top:]]
2931 wvl = wvl[isrt[-top:]]
2932 lvl1 = lvl1[isrt[-top:]]
2933 lvl2 = lvl2[isrt[-top:]]
2934 obs = obs[isrt[-top:]]
2935 emiss = emiss[isrt[-top:]]
2936 avalue = avalue[isrt[-top:]]
2937 pretty1 = pretty1[isrt[-top:]]
2938 pretty2 = pretty2[isrt[-top:]]
2939
2940
2941
2942 if relative:
2943 emiss = emiss/emiss[:top].max()
2944
2945
2946 idx = np.argsort(wvl)
2947
2948 fmt = '%5s %5i %5i %25s - %25s %12.3f %12.3e %12.2e %1s'
2949 print( ' ')
2950 print( '------------------------------------------')
2951 print(' ')
2952 print(' Ion lvl1 lvl2 lower upper Wvl(A) Emissivity A value Obs')
2953 for kdx in idx:
2954 print(fmt%(ionS[kdx], lvl1[kdx], lvl2[kdx], pretty1[kdx], pretty2[kdx], wvl[kdx], emiss[kdx], avalue[kdx], obs[kdx]))
2955 print(' ')
2956 print(' ------------------------------------------')
2957 print(' ')
2958
2959 self.Emiss['wvlTop'] = wvl[idx]
2960 self.Emiss['emissTop'] = emiss[idx]
2961 if outFile:
2962 fmt = '%5s %5i %5i %25s - %25s %12.3f %12.3e %12.2e %1s'
2963 output.write(' \n')
2964 output.write(' ------------------------------------------ \n')
2965 output.write(' ')
2966 output.write(' Ion lvl1 lvl2 lower upper Wvl(A) Emissivity A value Obs \n')
2967 for kdx in idx:
2968 output.write(fmt%(ionS[kdx], lvl1[kdx], lvl2[kdx], pretty1[kdx], pretty2[kdx], wvl[kdx], emiss[kdx], avalue[kdx], obs[kdx]))
2969 output.write(' \n')
2970 output.write(' ------------------------------------------ \n')
2971 output.write(' \n ')
2972 output.close()
2973
2974
2975
2976 - def emissPlot(self, index=-1, wvlRange=None, top=10, linLog='lin', relative=0, verbose=0, plotFile = 0, saveFile=0 ):
2977 '''Plot the emissivities.
2978
2979 wvlRange, a 2 element tuple, list or array determines the wavelength range
2980
2981 Top specifies to plot only the top strongest lines, default = 10
2982
2983 linLog specifies a linear or log plot, want either lin or log, default = lin
2984
2985 normalize = 1 specifies whether to normalize to strongest line, default = 0'''
2986
2987 title=self.Spectroscopic
2988
2989
2990 if hasattr(self, 'Emiss'):
2991 em = self.Emiss
2992 else:
2993 try:
2994 self.emiss()
2995 em = self.Emiss
2996 except:
2997 print(' emissivities not calculated and emiss() is unable to calculate them')
2998 print(' perhaps the temperature and/or eDensity are not set')
2999 return
3000 emiss = em['emiss']
3001 wvl = em['wvl']
3002 temperature = self.Temperature
3003 eDensity = self.EDensity
3004
3005 ndens = eDensity.size
3006 ntemp = temperature.size
3007
3008 if ndens == 1 and ntemp == 1:
3009 dstr = ' - Density = %10.2e (cm$^{-3}$)' %(eDensity)
3010 tstr = ' - T = %10.2e (K)' %(temperature)
3011 elif ndens == 1 and ntemp > 1:
3012 if index < 0:
3013 index = ntemp/2
3014 print('using index = %5i specifying temperature = %10.2e'%(index, temperature[index]))
3015 self.Message = 'using index = %5i specifying temperature = %10.2e'%(index, temperature[index])
3016
3017
3018
3019
3020 emiss=emiss[:, index]
3021 dstr=' - Density = %10.2e (cm$^{-3}$)' % eDensity
3022 tstr=' - T = %10.2e (K)' % temperature[index]
3023 elif ndens > 1 and ntemp == 1:
3024 if index < 0:
3025 index = ntemp/2
3026 print('using index =%5i specifying eDensity = %10.2e'%(index, eDensity[index]))
3027 self.Message = 'using index =%5i specifying eDensity = %10.2e'%(index, eDensity[index])
3028
3029
3030
3031
3032 emiss=emiss[:, index]
3033 dstr=' - Density = %10.2e (cm$^{-3}$)' % eDensity[index]
3034 tstr=' - T = %10.2e (K)' % temperature
3035 elif ndens > 1 and ntemp > 1:
3036 if index < 0:
3037 index = ntemp/2
3038 print('using index = %5i specifying temperature = %10.2e, eDensity = %10.2e'%(index, temperature[index], eDensity[index]))
3039 self.Message = 'using index = %5i specifying temperature = %10.2e, eDensity = %10.2e'%(index, temperature[index], eDensity[index])
3040
3041
3042
3043
3044 emiss=emiss[:, index]
3045 dstr=' - Density = %10.2e (cm$^{-3}$)' % eDensity[index]
3046 tstr=' - T = %10.2e (K)' % temperature[index]
3047 if type(wvlRange) != type(None):
3048 wvlIndex = util.between(wvl, wvlRange)
3049 else:
3050 wvlIndex = range(wvl.size)
3051 emiss = emiss[wvlIndex]
3052 wvl = wvl[wvlIndex]
3053
3054 self.Error = 0
3055 if wvl.size == 0:
3056 print('No lines in this wavelength interval')
3057 self.Error = 1
3058 self.Message = 'No lines in this wavelength interval'
3059
3060
3061
3062
3063
3064 return
3065 elif top == 0:
3066 top = wvl.size
3067 elif wvl.size > top:
3068 isrt = np.argsort(emiss)
3069 wvl = wvl[isrt[-top:]]
3070 emiss = emiss[isrt[-top:]]
3071 else:
3072 top = wvl.size
3073
3074
3075 pl.figure()
3076 ylabel = 'Emissivity'
3077 if relative:
3078 emiss = emiss/emiss[:top].max()
3079 ylabel += ' (Relative)'
3080
3081 xlabel = 'Wavelength ('+self.Defaults['wavelength'] +')'
3082
3083 ymin = 10.**(np.log10(emiss.min()).round(0)-0.5 )
3084
3085 pl.ion()
3086
3087
3088
3089
3090
3091 for idx in range(top):
3092 xx=[wvl[idx], wvl[idx]]
3093 if linLog == 'lin':
3094 yy=[0., emiss[idx]]
3095 pl.plot(xx, yy)
3096 else:
3097 yy=[ymin/10., emiss[idx]]
3098 pl.semilogy(xx, yy)
3099 pl.xlabel(xlabel)
3100 pl.ylabel(ylabel)
3101 pl.title(title+tstr+dstr)
3102 if wvlRange:
3103 pl.axis([wvlRange[0], wvlRange[1], ymin, emiss.max()])
3104 if plotFile:
3105 pl.savefig(plotFile)
3106
3107 idx = np.argsort(wvl)
3108 self.Emiss['wvlTop'] = wvl[idx]
3109 self.Emiss['emissTop'] = emiss[idx]
3110
3111
3112
3113 - def emissRatio(self,wvlRange=None, wvlRanges=None,top=10):
3114 """
3115 Plot the ratio of 2 lines or sums of lines.
3116 Shown as a function of density and/or temperature.
3117 For a single wavelength range, set wvlRange = [wMin, wMax]
3118 For multiple wavelength ranges, set wvlRanges = [[wMin1,wMax1],[wMin2,wMax2], ...]
3119 A plot of relative emissivities is shown and then a dialog appears for the user to
3120 choose a set of lines.
3121 """
3122
3123
3124
3125
3126 if hasattr(self, 'Emiss'):
3127 doEmiss = False
3128 em = self.Emiss
3129 else:
3130 doEmiss = True
3131
3132
3133 if doEmiss:
3134
3135 self.emiss()
3136 em = self.Emiss
3137
3138
3139 fontsize=14
3140
3141
3142 eDensity = self.EDensity
3143 emiss = em['emiss']
3144 ionS = em['ionS']
3145 wvl = em["wvl"]
3146 lineLabel = []
3147 for iline, ions in enumerate(ionS):
3148 lineLabel.append(ions+' '+str(wvl[iline]))
3149
3150 plotLabels = em["plotLabels"]
3151 xLabel = plotLabels["xLabel"]
3152 yLabel = plotLabels["yLabel"]
3153
3154
3155
3156
3157 if wvlRange:
3158 igvl=util.between(wvl,wvlRange)
3159 elif wvlRanges:
3160 igvl = []
3161 for awvlRange in wvlRanges:
3162 igvl.extend(util.between(wvl,awvlRange))
3163 else:
3164 igvl=range(len(wvl))
3165
3166 nlines=len(igvl)
3167
3168
3169
3170 igvl=np.take(igvl,wvl[igvl].argsort())
3171
3172
3173 if top > nlines:
3174 top=nlines
3175
3176 maxEmiss = np.zeros(nlines,'Float64')
3177 print(' maxEmiss.shape = %s'%(str(maxEmiss.shape)))
3178 for iline in range(nlines):
3179 maxEmiss[iline] = emiss[igvl[iline]].max()
3180 for iline in range(nlines):
3181 if maxEmiss[iline] == maxEmiss.max():
3182 maxAll = emiss[igvl[iline]]
3183
3184 igvlsort=np.take(igvl,np.argsort(maxEmiss))
3185
3186 topLines=igvlsort[-top:]
3187
3188 maxWvl='%5.3f' % wvl[topLines[-1]]
3189
3190
3191 topLines=topLines[wvl[topLines].argsort()]
3192
3193
3194
3195 good = np.where(emiss > 0.)
3196 emissMin=emiss[good].min()
3197 bad=np.where(emiss <= 0.)
3198 emiss[bad]=emissMin
3199
3200
3201 ntemp=self.Temperature.size
3202
3203 ndens=self.EDensity.size
3204
3205 ylabel='Emissivity relative to '+maxWvl
3206 title=self.Spectroscopic
3207
3208
3209 if ndens==1 and ntemp==1:
3210 print(' only a single temperature and eDensity')
3211 return
3212 elif ndens == 1:
3213 xlabel='Temperature (K)'
3214 xvalues=self.Temperature
3215 outTemperature=self.Temperature
3216 outDensity=np.zeros(ntemp,'Float64')
3217 outDensity.fill(self.EDensity)
3218 desc_str=' at Density = %10.2e (cm)$^{-3}$' % self.EDensity
3219 elif ntemp == 1:
3220 xvalues=self.EDensity
3221 outTemperature=np.zeros(ndens,'Float64')
3222 outTemperature.fill(self.Temperature)
3223 outDensity=self.EDensity
3224 xlabel=r'$\rm{Electron Density (cm)^{-3}}$'
3225 desc_str=' at Temp = %10.2e (K)' % self.Temperature
3226 else:
3227 outTemperature=self.Temperature
3228 outDensity=self.EDensity
3229 xlabel='Temperature (K)'
3230 xvalues=self.Temperature
3231 desc_str=' for variable Density'
3232
3233
3234
3235 pl.ion()
3236
3237
3238
3239
3240
3241
3242 ymax = np.max(emiss[topLines[0]]/maxAll)
3243 ymin = ymax
3244 pl.figure()
3245 ax = pl.subplot(111)
3246 nxvalues=len(xvalues)
3247 for iline in range(top):
3248 tline=topLines[iline]
3249 pl.loglog(xvalues,emiss[tline]/maxAll)
3250 if np.min(emiss[tline]/maxAll) < ymin:
3251 ymin = np.min(emiss[tline]/maxAll)
3252 if np.max(emiss[tline]/maxAll) > ymax:
3253 ymax = np.max(emiss[tline]/maxAll)
3254 skip=2
3255 start=divmod(iline,nxvalues)[1]
3256 for ixvalue in range(start,nxvalues,nxvalues//skip):
3257 pl.text(xvalues[ixvalue],emiss[tline,ixvalue]/maxAll[ixvalue],str(wvl[tline]))
3258 pl.xlim(xvalues.min(),xvalues.max())
3259
3260 pl.xlabel(xlabel,fontsize=fontsize)
3261 pl.ylabel(ylabel,fontsize=fontsize)
3262 if ndens == ntemp and ntemp > 1:
3263 pl.text(0.07, 0.5,title, horizontalalignment='left', verticalalignment='center', fontsize=fontsize, transform = ax.transAxes)
3264
3265 ax2 = pl.twiny()
3266 xlabelDen=r'Electron Density (cm$^{-3}$)'
3267 pl.xlabel(xlabelDen, fontsize=fontsize)
3268 pl.loglog(eDensity,emiss[topLines[top-1]]/maxAll, visible=False)
3269 ax2.xaxis.tick_top()
3270 pl.ylim(ymin/1.2, 1.2*ymax)
3271 else:
3272 pl.ylim(ymin/1.2, 1.2*ymax)
3273 pl.title(title+desc_str,fontsize=fontsize)
3274 pl.draw()
3275
3276 time.sleep(0.5)
3277
3278
3279
3280 selectTags = []
3281 for itop in topLines:
3282 selectTags.append(ionS[itop]+ ' '+ str(wvl[itop]))
3283
3284 numden = chGui.gui.choice2Dialog(selectTags)
3285
3286
3287
3288
3289 num_idx=numden.numIndex
3290 if len(num_idx) == 0:
3291 print(' no numerator lines were selected')
3292 return
3293
3294 den_idx=numden.denIndex
3295 if len(den_idx) == 0:
3296 print(' no denominator lines were selected')
3297 return
3298
3299 numEmiss=np.zeros(len(xvalues),'Float64')
3300 for aline in num_idx:
3301 numEmiss+=emiss[topLines[aline]]
3302
3303 denEmiss=np.zeros(len(xvalues),'Float64')
3304 for aline in den_idx:
3305 denEmiss+=emiss[topLines[aline]]
3306
3307
3308
3309 pl.figure()
3310 ax = pl.subplot(111)
3311 pl.loglog(xvalues,numEmiss/denEmiss)
3312 pl.xlim(xvalues.min(),xvalues.max())
3313 pl.xlabel(xlabel,fontsize=fontsize)
3314 pl.ylabel('Emissivity Ratio ('+self.Defaults['flux']+')',fontsize=fontsize)
3315 desc = ''
3316 for aline in num_idx:
3317 desc += ' ' + selectTags[aline]
3318
3319 desc +=' / '
3320 for aline in den_idx:
3321 desc += ' ' + selectTags[aline]
3322
3323 if ndens == ntemp and ntemp > 1:
3324 pl.text(0.07, 0.5,desc, horizontalalignment='left', verticalalignment='center', fontsize=fontsize, transform = ax.transAxes)
3325
3326 ax2 = pl.twiny()
3327 xlabelDen=r'Electron Density (cm$^{-3}$)'
3328 pl.xlabel(xlabelDen, fontsize=fontsize)
3329 pl.loglog(eDensity,numEmiss/denEmiss, visible=False)
3330 ax2.xaxis.tick_top()
3331 else:
3332
3333 pl.title(desc,fontsize=fontsize)
3334
3335
3336
3337
3338
3339
3340
3341 intensityRatioFileName=self.IonStr
3342 for aline in num_idx:
3343 intensityRatioFileName+= '_%3i'%(wvl[topLines[aline]])
3344 intensityRatioFileName+='_2'
3345 for aline in den_idx:
3346 intensityRatioFileName+= '_%3i'%(wvl[topLines[aline]])
3347 intensityRatioFileName+='.rat'
3348 self.IntensityRatio={'ratio':numEmiss/denEmiss,'desc':desc,
3349 'temperature':outTemperature,'eDensity':outDensity,'filename':intensityRatioFileName, 'numIdx':num_idx, 'denIdx':den_idx}
3350
3351
3352
3353 - def intensity(self, wvlRange = None, allLines=1, em=0):
3354 """
3355 Calculate the intensities for lines of the specified ion.
3356
3357 wvlRange, a 2 element tuple, list or array determines the wavelength range
3358
3359 units: ergs cm^-3 s^-1 str^-1
3360
3361 includes elemental abundance and ionization fraction.
3362
3363 the emission measure 'em' is included if specified
3364 """
3365
3366
3367 if type(em) == int and em == 0:
3368 if hasattr(self, 'Em'):
3369 em = self.Em
3370 else:
3371 em = np.ones(self.NTempDen, 'float64')
3372 self.Em = em
3373 elif type(em) == float and em > 0.:
3374 em = np.ones(self.NTempDen, 'float64')*em
3375 self.Em = em
3376 elif type(em) == list or type(em) == tuple or type(em) == np.ndarray:
3377 em = np.asarray(em, 'float64')
3378 self.Em = em
3379
3380
3381 if not hasattr(self, 'Emiss'):
3382 self.emiss(wvlRange = wvlRange, allLines=allLines)
3383 emiss = copy.copy(self.Emiss)
3384 else:
3385 emiss = copy.copy(self.Emiss)
3386 if 'errorMessage' in emiss.keys():
3387 self.Intensity = {'errorMessage': self.Spectroscopic+' no lines in this wavelength region'}
3388 return
3389
3390
3391 emissivity = emiss['emiss']
3392 ionS = emiss['ionS']
3393 wvl = emiss['wvl']
3394 lvl1 = emiss['lvl1']
3395 lvl2 = emiss['lvl2']
3396 obs = emiss['obs']
3397 pretty1 = emiss['pretty1']
3398 pretty2 = emiss['pretty2']
3399 avalue = emiss['avalue']
3400
3401
3402
3403
3404
3405
3406
3407 if hasattr(self, 'Abundance'):
3408 ab=self.Abundance
3409 else:
3410 self.Abundance = io.abundanceRead()
3411 ab=self.Abundance
3412 if hasattr(self, 'IoneqOne'):
3413 thisIoneq=self.IoneqOne
3414 else:
3415 self.ioneqOne()
3416 thisIoneq=self.IoneqOne
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429 if len(emissivity.shape) > 1:
3430 nwvl, ntempden= emissivity.shape
3431 intensity = np.zeros((ntempden, nwvl),'Float64')
3432 if thisIoneq.size == 1:
3433 thisIoneq = np.ones(ntempden, 'float64')*thisIoneq
3434 for it in range(ntempden):
3435 intensity[it] = ab*thisIoneq[it]*emissivity[:, it]*em[it]/self.EDensity[it]
3436 else:
3437 nwvl=len(emissivity)
3438 ntempden=1
3439 intensity = ab*thisIoneq*emissivity*em/self.EDensity
3440 if ntempden == 1:
3441 integrated = intensity
3442 else:
3443 integrated = intensity.sum(axis=0)
3444 Intensity = {'intensity':intensity, 'integrated':integrated,'ionS':ionS, 'wvl':wvl, 'lvl1':lvl1, 'lvl2':lvl2, 'pretty1':pretty1, 'pretty2':pretty2, 'obs':obs, 'avalue':avalue, 'em':em}
3445 self.Intensity = Intensity
3446
3447
3448
3450 """
3451 Calculate the summed radiative loss rate for all lines of the specified ion.
3452
3453 wvlRange, a 2 element tuple, list or array determines the wavelength range
3454
3455 units: ergs cm^-3 s^-1
3456
3457 includes elemental abundance and ionization fraction.
3458 """
3459
3460
3461 self.emiss(wvlRange = wvlRange, allLines=allLines)
3462 emiss = self.Emiss
3463 if 'errorMessage' in emiss.keys():
3464 self.Intensity = {'errorMessage': self.Spectroscopic+' no lines in this wavelength region'}
3465 return
3466 em = emiss['emiss']
3467 wvl = emiss['wvl']
3468 eDensity = self.EDensity
3469 if hasattr(self, 'Abundance'):
3470 ab=self.Abundance
3471 else:
3472 self.Abundance = io.abundanceRead()
3473 ab=self.Abundance
3474 if hasattr(self, 'IoneqOne'):
3475 thisIoneq=self.IoneqOne
3476 else:
3477 self.ioneqOne()
3478 thisIoneq=self.IoneqOne
3479 try:
3480 nwvl, ntempden = em.shape
3481 intensity = np.zeros((ntempden, nwvl),'Float64')
3482 if thisIoneq.size == 1:
3483 thisIoneq = np.ones(ntempden, 'float64')*thisIoneq
3484 for it in range(ntempden):
3485 if self.Defaults['flux'] != 'energy':
3486 intensity[it] = 4.*const.pi*(const.planck*const.light*1.e+8/wvl)*ab*thisIoneq[it]*em[:, it]
3487 else:
3488 intensity[it] = 4.*const.pi*ab*thisIoneq[it]*em[:, it]/eDensity[it]
3489 loss = intensity.sum(axis=1)
3490 except:
3491 nwvl=len(em)
3492 ntempden=1
3493 if self.Defaults['flux'] != 'energy':
3494 intensity = 4.*const.pi*(const.planck*const.light*1.e+8/wvl)*ab*thisIoneq*em
3495 else:
3496 intensity = 4.*const.pi*ab*thisIoneq*em
3497 loss = intensity.sum()
3498 self.BoundBoundLoss = {'rate':loss, 'wvlRange':wvlRange, 'temperature':self.Temperature, 'eDensity':self.EDensity}
3499
3500
3501
3503 '''
3504 to take a set of date and interpolate against the IntensityRatio
3505 the scale can be one of 'lin'/'linear' [default], 'loglog', 'logx', 'logy',
3506 '''
3507
3508 if self.IntensityRatio['temperature'].max() > self.IntensityRatio['temperature'].min():
3509 x = self.IntensityRatio['ratio']
3510 y = self.IntensityRatio['temperature']
3511 if verbose:
3512 print('using temperature with %i5 values'%(len(x)))
3513 print(' number of values')
3514 else:
3515 x = self.IntensityRatio['ratio']
3516 y = self.IntensityRatio['eDensity']
3517
3518 if x[0] > x[-1]:
3519 x = sorted(x)
3520 sy = []
3521 for idx in range(len(y) -1, -1, -1):
3522 sy.append(y[idx])
3523 else:
3524 sy = y
3525
3526 if 'lin' in scale:
3527 y2 = interpolate.splrep(x, sy, s=0)
3528 interpolatedData = interpolate.splev(data,y2)
3529 if plot:
3530 pl.plot(sy, x)
3531 pl.plot(interpolatedData, data, 'bD')
3532 elif scale == 'loglog':
3533 y2 = interpolate.splrep(np.log(x), np.log(sy), s=0)
3534 interpolatedData = np.exp(interpolate.splev(np.log(data),y2))
3535 if plot:
3536 pl.loglog(sy, x)
3537 pl.loglog(interpolatedData, data, 'bD')
3538 elif scale == 'logx':
3539 y2 = interpolate.splrep(x, np.log(sy), s=0)
3540 interpolatedData = np.exp(interpolate.splev(data,y2))
3541 if plot:
3542 pl.semilogx(sy, x)
3543 pl.semilogx(interpolatedData, data, 'bD')
3544 elif scale == 'logy':
3545 y2 = interpolate.splrep(np.log(x), sy, s=0)
3546 interpolatedData = interpolate.splev(np.log(data),y2)
3547 if plot:
3548 pl.semilogy(sy, x)
3549 pl.semilogy(interpolatedData, data, 'bD')
3550 else:
3551 print(' scale not understood = %s'%(scale))
3552 for i, avalue in enumerate(interpolatedData):
3553 print(' data, value = %12.3e %12.3e'%(data[i], avalue))
3554 self.IntensityRatioInterpolated = {'data':data, 'value':interpolatedData}
3555
3556
3557
3559 '''
3560 Provide the ionization equilibrium for the selected ion as a function of temperature.
3561 returned in self.IoneqOne
3562 '''
3563
3564 if hasattr(self, 'Temperature'):
3565 temperature = self.Temperature
3566 else:
3567 return
3568
3569 if hasattr(self, 'IoneqAll'):
3570 ioneqAll = self.IoneqAll
3571 else:
3572 ioneqAll = io.ioneqRead(ioneqname = self.Defaults['ioneqfile'])
3573
3574 self.ioneqAll=self.IoneqAll
3575
3576 ioneqTemperature = ioneqAll['ioneqTemperature']
3577 Z=self.Z
3578 Ion=self.Ion
3579 Dielectronic=self.Dielectronic
3580 ioneqOne = np.zeros_like(temperature)
3581
3582 thisIoneq=ioneqAll['ioneqAll'][Z-1,Ion-1 + Dielectronic].squeeze()
3583 del ioneqAll
3584
3585 gioneq=thisIoneq > 0.
3586 goodt1=self.Temperature >= ioneqTemperature[gioneq].min()
3587 goodt2=self.Temperature <= ioneqTemperature[gioneq].max()
3588 goodt=np.logical_and(goodt1,goodt2)
3589 y2=interpolate.splrep(np.log(ioneqTemperature[gioneq]),np.log(thisIoneq[gioneq]),s=0)
3590
3591 if goodt.sum() > 0:
3592 if self.Temperature.size > 1:
3593 gIoneq=interpolate.splev(np.log(self.Temperature[goodt]),y2)
3594 ioneqOne[goodt] = np.exp(gIoneq)
3595 else:
3596 gIoneq=interpolate.splev(np.log(self.Temperature),y2)
3597 ioneqOne = np.exp(gIoneq)
3598 self.IoneqOne = ioneqOne
3599
3600
3601
3602 - def gofnt(self,wvlRange=0,top=10, verbose=0):
3603 """
3604 Calculate the 'so-called' G(T) function.
3605
3606 Given as a function of both temperature and eDensity.
3607
3608 Only the top( set by 'top') brightest lines are plotted.
3609 the G(T) function is returned in a dictionary self.Gofnt
3610 """
3611
3612
3613
3614
3615 if hasattr(self, 'Emiss'):
3616 em=copy.copy(self.Emiss)
3617 else:
3618 self.emiss()
3619 em=copy.copy(self.Emiss)
3620
3621
3622
3623
3624
3625
3626
3627 if not hasattr(self, 'Abundance'):
3628 self.Abundance = io.abundanceRead()
3629
3630 fontsize=12
3631
3632 emiss=em["emiss"]
3633 wvl=em["wvl"]
3634 pretty1 = em['pretty1']
3635 pretty2 = em['pretty2']
3636 lvl1 = em['lvl1']
3637 lvl2 = em['lvl2']
3638
3639
3640
3641 plotLabels = em["plotLabels"]
3642 xLabel = plotLabels["xLabel"]
3643 yLabel = plotLabels["yLabel"]
3644
3645
3646
3647
3648 if type(wvlRange) != type(1):
3649 igvl=util.between(wvl,wvlRange)
3650 else:
3651 igvl=range(len(wvl))
3652 nlines=len(igvl)
3653 if nlines ==0:
3654 print(' no lines in selected interval')
3655 return
3656
3657
3658 if top > nlines:
3659 top=nlines
3660 maxEmiss=np.zeros(nlines,'Float64')
3661 for iline in range(nlines):
3662 maxEmiss[iline]=emiss[igvl[iline]].max()
3663 for iline in range(nlines):
3664 if maxEmiss[iline]>=maxEmiss.max():
3665 maxAll=emiss[igvl[iline]]
3666
3667
3668
3669 igvlsort=np.take(igvl,np.argsort(maxEmiss))
3670 topLines=igvlsort[-top:]
3671 maxWvl='%5.3f' % wvl[topLines[-1]]
3672
3673
3674
3675 good = np.where(emiss > 0.)
3676 emissMin=emiss[good].min()
3677 bad=np.where(emiss <= 0.)
3678 emiss[bad]=emissMin
3679
3680 topLines=topLines[wvl[topLines].argsort()]
3681
3682
3683 eDensity = self.EDensity
3684 temperature = self.Temperature
3685
3686
3687 ntemp = temperature.size
3688 if ntemp > 0:
3689 if temperature[0] == temperature[-1]:
3690 ntemp = 1
3691
3692 ndens = eDensity.size
3693 if ndens > 0:
3694 if eDensity[0] == eDensity[-1]:
3695 ndens = 1
3696
3697 print(' ndens = %5i ntemp = %5i'%(ndens, ntemp))
3698
3699 ylabel = 'Emissivity relative to '+maxWvl
3700 title = self.Spectroscopic
3701
3702
3703 if ndens==1 and ntemp==1:
3704 print(' only a single temperature and eDensity')
3705 return
3706 elif ndens == 1:
3707 xlabel='Temperature (K)'
3708 ngofnt = temperature.size
3709 xvalues=temperature
3710 outTemperature=temperature
3711 outDensity = eDensity
3712 desc_str=' at Density = %10.2e' % eDensity[0]
3713 elif ntemp == 1:
3714 xvalues=eDensity
3715 ngofnt = eDensity.size
3716 outTemperature=np.zeros(ndens,'Float64')
3717 outTemperature.fill(temperature)
3718 outDensity=eDensity
3719 xlabel=r'$\rm{Electron Density (cm}^{-3}\rm{)}$'
3720 desc_str=' at Temperature = %10.2e' % temperature[0]
3721 else:
3722 outTemperature=temperature
3723 outDensity=eDensity
3724 xlabel='Temperature (K)'
3725 xvalues=temperature
3726 ngofnt = ntemp
3727 desc_str=' for variable Density'
3728
3729
3730
3731
3732 pl.ion()
3733
3734
3735
3736
3737
3738 pl.figure()
3739 ax = pl.subplot(111)
3740 nxvalues=len(xvalues)
3741
3742
3743
3744
3745 ymax = 1.2
3746
3747 ymin = ymax
3748 for iline in range(top):
3749 tline=topLines[iline]
3750 pl.loglog(xvalues,emiss[tline]/maxAll)
3751 if np.min(emiss[tline]/maxAll) < ymin:
3752 ymin = np.min(emiss[tline]/maxAll)
3753 skip=2
3754 start=divmod(iline,nxvalues)[1]
3755 for ixvalue in range(start,nxvalues,nxvalues//skip):
3756 pl.text(xvalues[ixvalue],emiss[tline,ixvalue]/maxAll[ixvalue],str(wvl[tline]))
3757 pl.xlim(xvalues.min(),xvalues.max())
3758 pl.ylim(ymin, ymax)
3759
3760
3761 pl.xlabel(xlabel,fontsize=fontsize)
3762 pl.ylabel(ylabel,fontsize=fontsize)
3763 if ndens == ntemp and ntemp > 1:
3764 pl.text(0.07, 0.5,title, horizontalalignment='left', verticalalignment='center', fontsize=fontsize, transform = ax.transAxes)
3765
3766 ax2 = pl.twiny()
3767 xlabelDen=r'Electron Density (cm$^{-3}$)'
3768 pl.xlabel(xlabelDen, fontsize=fontsize)
3769 pl.loglog(eDensity,emiss[topLines[top-1]]/maxAll, visible=False)
3770 ax2.xaxis.tick_top()
3771 else:
3772 pl.ylim(ymin, ymax)
3773 pl.title(title+desc_str,fontsize=fontsize)
3774 pl.draw()
3775
3776 time.sleep(0.5)
3777
3778
3779 wvlChoices = []
3780 for iline in range(top):
3781 tline = topLines[iline]
3782 wvlChoices.append('%12.4f %4i %4i %s - %s'%(wvl[tline], lvl1[tline], lvl2[tline], pretty1[tline], pretty2[tline]))
3783 gline = chGui.gui.selectorDialog(wvlChoices,label='Select line(s)')
3784 gline_idx=gline.selectedIndex
3785
3786
3787 gAbund=self.Abundance
3788
3789 try:
3790 thisIoneq=self.IoneqOne
3791 except:
3792 self.ioneqOne()
3793 thisIoneq=self.IoneqOne
3794 if verbose:
3795 print(' abundance = %10.2e '%(gAbund))
3796 print(' index temperature ion fraction')
3797 for it, anioneq in enumerate(thisIoneq):
3798 print (' %5i %10.2e %10.2e '%(it, outTemperature[it], anioneq))
3799
3800
3801
3802 gIoneq=self.IoneqOne/eDensity
3803
3804
3805
3806
3807 pl.figure()
3808 g_line = topLines[gline_idx]
3809
3810
3811 gofnt=np.zeros(ngofnt,'float64')
3812 for aline in g_line:
3813 gofnt+=gAbund*gIoneq*emiss[aline].squeeze()
3814 self.Gofnt={'temperature':outTemperature,'eDensity':outDensity,'gofnt':gofnt, 'index':g_line, 'wvl':wvl[g_line]}
3815
3816 pl.loglog(xvalues,gofnt)
3817 pl.xlim(xvalues.min(),xvalues.max())
3818 pl.xlabel(xlabel,fontsize=fontsize)
3819 pl.ylabel('Gofnt',fontsize=fontsize)
3820 newTitle = '%9s'%(self.Spectroscopic) + '%12.3f %4i %4i %s - %s'%(wvl[g_line[0]], lvl1[g_line[0]], lvl2[g_line[0]], pretty1[g_line[0]], pretty2[g_line[0]])
3821 if len(g_line) > 1:
3822 newTitle +='\n'
3823 for igl in g_line[1:]:
3824 newTitle += ' ' + '%12.3f %4i %4i %s - %s'%(wvl[igl], lvl1[igl], lvl2[igl], pretty1[igl], pretty2[igl])
3825 if igl != g_line[-1]:
3826 newTitle +='\n'
3827
3828 pl.annotate(newTitle, xy=(-10, 10),
3829 xycoords='axes points',
3830 horizontalalignment='right', verticalalignment='bottom')
3831 if ndens == ntemp and ntemp > 1:
3832
3833 pl.text(0.07, 0.5,newTitle, horizontalalignment='left', verticalalignment='center', fontsize=fontsize, transform = ax.transAxes)
3834
3835 ax2 = pl.twiny()
3836
3837 pl.xlabel(xlabelDen, fontsize=fontsize)
3838 pl.loglog(eDensity,gofnt, visible=False)
3839 ax2.xaxis.tick_top()
3840 else:
3841 pl.title(newTitle, fontsize=fontsize)
3842
3843
3844
3845
3846
3847
3849 ''' to calculate the two-photon continuum rate coefficient - only for hydrogen- and helium-like ions'''
3850 wvl = np.array(wvl, 'float64')
3851 nWvl = wvl.size
3852 if self.Z -self.Ion > 1 or self.Dielectronic:
3853
3854 self.TwoPhoton = {'emiss':np.zeros(nWvl, 'float4'), 'wvl':wvl}
3855 return
3856 else:
3857 try:
3858 pop = self.Population['population']
3859 nTempDens = max(self.Temperature.size, self.EDensity.size)
3860 except:
3861 self.populate()
3862 pop = self.Population['population']
3863 nTempDens = max(self.Temperature.size, self.EDensity.size)
3864 if nTempDens > 1:
3865 emiss = np.zeros((nTempDens, nWvl), 'float64')
3866 if self.EDensity.size == 1:
3867 eDensity = np.repeat(self.EDensity, nTempDens)
3868 else:
3869 eDensity = self.EDensity
3870 else:
3871 emiss = np.zeros(nWvl, 'float64')
3872 eDensity = self.EDensity
3873 if self.Z == self.Ion:
3874
3875 l1 = 1-1
3876 l2 = 2 - 1
3877 wvl0 = 1.e+8/(self.Elvlc['ecm'][l2] - self.Elvlc['ecm'][l1])
3878 goodWvl = wvl > wvl0
3879 y = wvl0/wvl[goodWvl]
3880 dist = io.twophotonHRead()
3881 avalue = dist['avalue'][self.Z-1]
3882 asum = dist['asum'][self.Z-1]
3883 distr1 = interpolate.splrep(dist['y0'], dist['psi0'][self.Z-1], s=0)
3884 distr = avalue*y*interpolate.splev(y, distr1)/(asum*wvl[goodWvl])
3885 if self.Defaults['flux'] == 'energy':
3886 f = (const.light*const.planck*1.e+8)/wvl[goodWvl]
3887 else:
3888 f=1.
3889 if nTempDens == 1:
3890 emiss[goodWvl] = f*pop[l2]*distr/self.EDensity
3891 else:
3892 for it in range(nTempDens):
3893 emiss[it, goodWvl] = f*pop[it, l2]*distr/self.EDensity[it]
3894 self.TwoPhotonEmiss = {'wvl':wvl, 'emiss':emiss}
3895 else:
3896
3897 l1 = 1-1
3898 l2 = heseqLvl2[self.Z -1] - 1
3899 wvl0 = 1.e+8/(self.Elvlc['ecm'][l2] - self.Elvlc['ecm'][l1])
3900 goodWvl = wvl > wvl0
3901 y = wvl0/wvl[goodWvl]
3902 dist = io.twophotonHeRead()
3903 avalue = dist['avalue'][self.Z-1]
3904 distr1 = interpolate.splrep(dist['y0'], dist['psi0'][self.Z-1], s=0)
3905 distr = avalue*y*interpolate.splev(y, distr1)/wvl[goodWvl]
3906 if self.Defaults['flux'] == 'energy':
3907 f = (const.light*const.planck*1.e+8)/wvl[goodWvl]
3908 else:
3909 f=1.
3910 if nTempDens == 1:
3911 emiss[goodWvl] = f*pop[l2]*distr/self.EDensity
3912 else:
3913 for it in range(nTempDens):
3914 emiss[it, goodWvl] = f*pop[it, l2]*distr/self.EDensity[it]
3915 self.TwoPhotonEmiss = {'wvl':wvl, 'emiss':emiss}
3916
3917
3918
3920 '''
3921 to calculate the two-photon continuum - only for hydrogen- and helium-like ions
3922 includes the elemental abundance and the ionization equilibrium
3923 includes the emission measure if specified
3924 '''
3925 wvl = np.array(wvl, 'float64')
3926
3927 if type(em) == int and em == 0:
3928 if hasattr(self, 'Em'):
3929 em = self.Em
3930 else:
3931 em = np.ones(self.NTempDen, 'float64')
3932 self.Em = em
3933 elif type(em) == float and em > 0.:
3934 em = np.ones(self.NTempDen, 'float64')*em
3935 self.Em = em
3936 elif type(em) == list or type(em) == tuple or type(em) == np.ndarray:
3937 em = np.asarray(em, 'float64')
3938 self.Em = em
3939
3940
3941 nWvl = wvl.size
3942 if self.Z - self.Ion > 1 or self.Dielectronic:
3943
3944 print(' not doing 2 photon for %s'%(self.IonStr))
3945 self.TwoPhoton = {'emiss':np.zeros(nWvl, 'float64'), 'wvl':wvl}
3946 return
3947 else:
3948 try:
3949 ab=self.Abundance
3950 except:
3951 self.Abundance = io.abundanceRead()
3952 ab=self.Abundance
3953 try:
3954 thisIoneq=self.IoneqOne
3955 except:
3956 self.ioneqOne()
3957 thisIoneq=self.IoneqOne
3958 try:
3959 pop = self.Population['population']
3960 nTempDens = max(self.Temperature.size, self.EDensity.size)
3961 except:
3962 self.populate()
3963 pop = self.Population['population']
3964 nTempDens = max(self.Temperature.size, self.EDensity.size)
3965 if nTempDens > 1:
3966 rate = np.zeros((nTempDens, nWvl), 'float64')
3967 if em.size == 1.:
3968 em = np.ones(nTempDens, 'float64')*em
3969 if self.EDensity.size == 1:
3970 eDensity = np.repeat(self.EDensity, nTempDens)
3971 else:
3972 eDensity = self.EDensity
3973 if self.Temperature.size == 1:
3974 temperature = np.repeat(self.Temperature, nTempDens)
3975 thisIoneq = np.repeat(thisIoneq, nTempDens)
3976 else:
3977 temperature = self.Temperature
3978 else:
3979 rate = np.zeros(nWvl, 'float64')
3980 eDensity = self.EDensity
3981 temperature = self.Temperature
3982 if self.Z == self.Ion:
3983
3984 l1 = 1-1
3985 l2 = 2 - 1
3986 wvl0 = 1.e+8/(self.Elvlc['ecm'][l2] - self.Elvlc['ecm'][l1])
3987 goodWvl = wvl > wvl0
3988 if goodWvl.sum() > 0:
3989 y = wvl0/wvl[goodWvl]
3990 dist = io.twophotonHRead()
3991 avalue = dist['avalue'][self.Z-1]
3992 asum = dist['asum'][self.Z-1]
3993 distr1 = interpolate.splrep(dist['y0'], dist['psi0'][self.Z-1], s=0)
3994 distr = avalue*y*interpolate.splev(y, distr1)/(asum*wvl[goodWvl])
3995 if self.Defaults['flux'] == 'energy':
3996 f = (const.light*const.planck*1.e+8)/(4.*const.pi*wvl[goodWvl])
3997 else:
3998 f=1./(4.*const.pi)
3999 if nTempDens == 1:
4000 rate[goodWvl] = f*pop[l2]*distr*ab*thisIoneq*em/eDensity
4001 else:
4002 for it in range(nTempDens):
4003 rate[it, goodWvl] = f*pop[it, l2]*distr*ab*thisIoneq[it]*em[it]/eDensity[it]
4004 self.TwoPhoton = {'wvl':wvl, 'rate':rate}
4005
4006 else:
4007
4008 l1 = 1-1
4009 l2 = heseqLvl2[self.Z -1] - 1
4010 wvl0 = 1.e+8/(self.Elvlc['ecm'][l2] - self.Elvlc['ecm'][l1])
4011 goodWvl = wvl > wvl0
4012 if goodWvl.sum() > 0:
4013 y = wvl0/wvl[goodWvl]
4014 dist = io.twophotonHeRead()
4015 avalue = dist['avalue'][self.Z-1]
4016 distr1 = interpolate.splrep(dist['y0'], dist['psi0'][self.Z-1], s=0)
4017 distr = avalue*y*interpolate.splev(y, distr1)/wvl[goodWvl]
4018 if self.Defaults['flux'] == 'energy':
4019 f = (const.light*const.planck*1.e+8)/(4.*const.pi*wvl[goodWvl])
4020 else:
4021 f=1./(4.*const.pi)
4022 if nTempDens == 1:
4023 rate[goodWvl] = f*pop[l2]*distr*ab*thisIoneq*em/eDensity
4024 else:
4025 for it in range(nTempDens):
4026 rate[it, goodWvl] = f*pop[it, l2]*distr*ab*thisIoneq[it]*em[it]/eDensity[it]
4027 self.TwoPhoton = {'wvl':wvl, 'rate':rate, 'em':em}
4028
4029
4030
4032 '''
4033 to calculate the two-photon energy loss rate - only for hydrogen- and helium-like ions
4034 includes the elemental abundance and the ionization equilibrium
4035 does not include the emission measure
4036 '''
4037 if self.Z -self.Ion > 1 or self.Dielectronic:
4038
4039 nTempDens = max(self.Temperature.size, self.EDensity.size)
4040
4041 rate = np.zeros((nTempDens), 'float64')
4042 self.TwoPhotonLoss = {'rate':rate}
4043 else:
4044 if hasattr(self, 'Abundance'):
4045 ab=self.Abundance
4046 else:
4047 self.Abundance = io.abundanceRead()
4048 ab=self.Abundance
4049 if hasattr(self, 'IoneqOne'):
4050 thisIoneq=self.IoneqOne
4051 else:
4052 self.ioneqOne()
4053 thisIoneq=self.IoneqOne
4054 if hasattr(self, 'Population'):
4055 pop = self.Population['population']
4056 nTempDens = max(self.Temperature.size, self.EDensity.size)
4057 else:
4058 self.populate()
4059 pop = self.Population['population']
4060 nTempDens = max(self.Temperature.size, self.EDensity.size)
4061 if nTempDens > 1:
4062 rate = np.zeros((nTempDens), 'float64')
4063 if self.EDensity.size == 1:
4064 eDensity = np.repeat(self.EDensity, nTempDens)
4065 else:
4066 eDensity = self.EDensity
4067 else:
4068 eDensity = self.EDensity
4069 if self.Z == self.Ion:
4070
4071 l1 = 1 - 1
4072 l2 = 2 - 1
4073 wvl0 = 1.e+8/(self.Elvlc['ecm'][l2] - self.Elvlc['ecm'][l1])
4074 dist = io.twophotonHRead()
4075 avalue = dist['avalue'][self.Z-1]
4076 f = (avalue*const.light*const.planck*1.e+8)/wvl0
4077 if nTempDens == 1:
4078 rate = f*pop[l2]*ab*thisIoneq/eDensity
4079 else:
4080 for it in range(nTempDens):
4081 rate[it] = f*pop[it, l2]*ab*thisIoneq[it]/eDensity[it]
4082 self.TwoPhotonLoss = {'temperature':self.Temperature,'eDensity':self.EDensity,'rate':rate}
4083 else:
4084
4085 l1 = 1 - 1
4086 l2 = heseqLvl2[self.Z -1] -1
4087 wvl0 = 1.e+8/(self.Elvlc['ecm'][l2] - self.Elvlc['ecm'][l1])
4088 dist = io.twophotonHeRead()
4089 avalue = dist['avalue'][self.Z-1]
4090 f = (avalue*const.light*const.planck*1.e+8)/wvl0
4091 if nTempDens == 1:
4092 rate = f*pop[l2]*ab*thisIoneq/eDensity
4093 else:
4094 for it in range(nTempDens):
4095 rate[it] = f*pop[it, l2]*ab*thisIoneq[it]/eDensity[it]
4096 self.TwoPhotonLoss = {'temperature':self.Temperature,'eDensity':self.EDensity,'rate':rate}
4097
4098
4099
4100
4101
4102
4104 """
4105 a class that contains methods to be used for 'Chianti on the Web'
4106 """
4108 """Provide a selection of lines for calculating the 'so-called' G(T) function.
4109
4110 Given as a function of both temperature and eDensity.
4111
4112 Only the top( set by 'top') brightest lines are plotted."""
4113
4114
4115
4116
4117 if hasattr(self, 'Emiss'):
4118 doEmiss=False
4119 em=self.Emiss
4120 else:
4121 doEmiss=True
4122
4123
4124 if doEmiss:
4125
4126 self.emiss()
4127 em=self.Emiss
4128
4129
4130
4131
4132
4133
4134
4135 if not hasattr(self, 'Abundance'):
4136 self.Abundance = io.abundanceRead()
4137
4138 fontsize=12
4139
4140 emiss=em["emiss"]
4141 wvl=em["wvl"]
4142 temperature=self.Temperature
4143 eDensity=self.EDensity
4144 plotLabels=em["plotLabels"]
4145 xLabel=plotLabels["xLabel"]
4146 yLabel=plotLabels["yLabel"]
4147
4148
4149
4150
4151 if type(wvlRange) != type(1):
4152 igvl=util.between(wvl,wvlRange)
4153 else:
4154 igvl=range(len(wvl))
4155 nlines=len(igvl)
4156 if nlines ==0:
4157 print(' no lines in selected interval')
4158 self.message = ' no lines in selected interval'
4159
4160
4161
4162
4163 return
4164
4165
4166 if (top > nlines) or (top == 0):
4167 top=nlines
4168 maxEmiss=np.zeros(nlines,'Float64')
4169 for iline in range(nlines):
4170 maxEmiss[iline]=emiss[igvl[iline]].max()
4171 for iline in range(nlines):
4172 if maxEmiss[iline]>=maxEmiss.max():
4173 maxAll=emiss[igvl[iline]]
4174 maxIndex = igvl[iline]
4175
4176 line=range(nlines)
4177 igvlsort=np.take(igvl,np.argsort(maxEmiss))
4178 topLines=igvlsort[-top:]
4179 maxWvl='%5.3f' % wvl[topLines[-1]]
4180 maxline=topLines[-1]
4181
4182
4183 good = np.where(emiss > 0.)
4184 emissMin=emiss[good].min()
4185 bad=np.where(emiss <= 0.)
4186 emiss[bad]=emissMin
4187
4188 topLines=topLines[wvl[topLines].argsort()]
4189
4190
4191 ntemp=self.Temperature.size
4192
4193 ndens=self.EDensity.size
4194
4195 ylabel = 'Emissivity relative to '+maxWvl
4196 title = self.Spectroscopic
4197
4198
4199 pl.ioff()
4200
4201
4202
4203
4204
4205
4206 if ndens==1 and ntemp==1:
4207 print(' only a single temperature and eDensity')
4208 return
4209 elif ndens == 1:
4210 xlabel='Temperature (K)'
4211 ngofnt = temperature.size
4212 xvalues=temperature
4213 outTemperature=temperature
4214 outDensity=np.zeros(ntemp,'Float64')
4215 outDensity.fill(eDensity)
4216 desc_str=' at Density = %10.2e' % eDensity
4217 elif ntemp == 1:
4218 xvalues=eDensity
4219 ngofnt = eDensity.size
4220 outTemperature=np.zeros(ndens,'Float64')
4221 outTemperature.fill(temperature)
4222 outDensity=eDensity
4223 xlabel=r'$\rm{Electron Density (cm}^{-3}\rm{)}$'
4224 desc_str=' at Temperature = %10.2e' % temperature
4225 else:
4226 outTemperature=temperature
4227 outDensity=eDensity
4228 xlabel='Temperature (K)'
4229 xvalues=temperature
4230
4231 desc_str=' for variable Density'
4232
4233
4234
4235
4236
4237 pl.figure()
4238 nxvalues=len(xvalues)
4239 for iline in range(top):
4240 tline=topLines[iline]
4241 pl.loglog(xvalues,emiss[tline]/maxAll)
4242 skip=2
4243 start=divmod(iline,nxvalues)[1]
4244 for ixvalue in range(start,nxvalues,nxvalues//skip):
4245 pl.text(xvalues[ixvalue],emiss[tline,ixvalue]/maxAll[ixvalue],str(wvl[tline]))
4246 pl.xlim(xvalues.min(),xvalues.max())
4247
4248
4249 pl.xlabel(xlabel,fontsize=fontsize)
4250 pl.ylabel(ylabel,fontsize=fontsize)
4251 pl.title(title+desc_str,fontsize=fontsize)
4252 if saveFile:
4253 pl.savefig(saveFile)
4254 else:
4255 pl.draw()
4256
4257
4258 wvlChoices = []
4259 for one in wvl[topLines]:
4260 wvlChoices.append('%12.3f'%(one))
4261 self.wvlChoices = wvlChoices
4262 self.topLines = topLines
4263
4264
4265
4266
4267
4268
4269 - def gofntPlot(self, wvlRange=0, top=10, index=0, saveFile=0):
4270 """Return a plot of the 'so-called' G(T) function fron the selected lines in index
4271
4272 Given as a function of both temperature and eDensity.
4273
4274 Only the top( set by 'top') brightest lines are plotted."""
4275
4276
4277
4278
4279 if hasattr(self, 'Emiss'):
4280 doEmiss=False
4281 em=self.Emiss
4282 else:
4283 doEmiss=True
4284
4285
4286 if doEmiss:
4287
4288 self.emiss()
4289 em=self.Emiss
4290
4291
4292
4293
4294
4295
4296
4297 if not hasattr(self, 'Abundance'):
4298 self.Abundance = io.abundanceRead()
4299
4300 fontsize=12
4301
4302 emiss=em["emiss"]
4303 wvl=em["wvl"]
4304 temperature=self.Temperature
4305 eDensity=self.EDensity
4306 plotLabels=em["plotLabels"]
4307 xLabel=plotLabels["xLabel"]
4308 yLabel=plotLabels["yLabel"]
4309
4310
4311
4312
4313 if type(wvlRange) != type(1):
4314 igvl=util.between(wvl,wvlRange)
4315 else:
4316 igvl=range(len(wvl))
4317 nlines=len(igvl)
4318 if nlines ==0:
4319 print(' no lines in selected interval')
4320 self.Message = ' no lines in selected interval '
4321 return
4322
4323
4324 if top > nlines:
4325 top=nlines
4326 maxEmiss=np.zeros(nlines,'Float64')
4327 for iline in range(nlines):
4328 maxEmiss[iline]=emiss[igvl[iline]].max()
4329 for iline in range(nlines):
4330 if maxEmiss[iline]>=maxEmiss.max():
4331 maxAll=emiss[igvl[iline]]
4332 maxIndex = igvl[iline]
4333 print(' maxIndex, maxAll = %5i %5i'%(maxIndex, maxAll))
4334
4335 igvlsort=np.take(igvl,np.argsort(maxEmiss))
4336 topLines=igvlsort[-top:]
4337
4338
4339
4340
4341 good = np.where(emiss > 0.)
4342 emissMin=emiss[good].min()
4343 bad=np.where(emiss <= 0.)
4344 emiss[bad]=emissMin
4345
4346 topLines=topLines[wvl[topLines].argsort()]
4347
4348
4349 ntemp=self.Temperature.size
4350
4351 ndens=self.EDensity.size
4352
4353
4354 title = self.Spectroscopic
4355
4356
4357 if ndens==1 and ntemp==1:
4358 print(' only a single temperature and eDensity')
4359 return
4360 elif ndens == 1:
4361 xlabel='Temperature (K)'
4362 ngofnt = temperature.size
4363 xvalues=temperature
4364 outTemperature=temperature
4365 outDensity=np.zeros(ntemp,'Float64')
4366 outDensity.fill(eDensity)
4367 desc_str=' at Density = %10.2e' % eDensity
4368 elif ntemp == 1:
4369 xvalues=eDensity
4370 ngofnt = eDensity.size
4371 outTemperature=np.zeros(ndens,'Float64')
4372 outTemperature.fill(temperature)
4373 outDensity=eDensity
4374 xlabel=r'$\rm{Electron Density (cm}^{-3}\rm{)}$'
4375 desc_str=' at Temperature = %10.2e' % temperature
4376 else:
4377 outTemperature=temperature
4378 outDensity=eDensity
4379 xlabel='Temperature (K)'
4380 xvalues=temperature
4381 ngofnt = temperature.size
4382 desc_str=' for variable Density'
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412 gline_idx = index
4413 nWvl = len(index)
4414
4415 gAbund=self.Abundance
4416
4417
4418
4419
4420
4421 if not hasattr(self, 'IoneqOne'):
4422 self.ioneqOne()
4423
4424
4425
4426
4427 gIoneq=self.IoneqOne/eDensity
4428
4429
4430
4431 pl.ioff()
4432
4433
4434
4435
4436
4437
4438
4439 pl.figure()
4440
4441
4442
4443 if nWvl > 1:
4444 gofnt=np.zeros((ngofnt) ,'float64')
4445
4446 for aline in gline_idx:
4447 gofnt += gAbund*gIoneq*emiss[aline].squeeze()
4448 else:
4449 gofnt = gAbund*gIoneq*emiss[index].squeeze()
4450
4451 self.Gofnt={'temperature':outTemperature,'eDensity':outDensity,'gofnt':gofnt, 'index':gline_idx}
4452
4453 pl.loglog(xvalues,gofnt)
4454 pl.xlim(xvalues.min(),xvalues.max())
4455 pl.xlabel(xlabel,fontsize=fontsize)
4456 pl.ylabel('Gofnt',fontsize=fontsize)
4457 pl.title(title+' '+str(wvl[index])+' '+desc_str, fontsize=fontsize)
4458 if saveFile:
4459 pl.savefig(saveFile)
4460 else:
4461 pl.show()
4462
4463
4464
4466 """Provide a selection of lines for calculating the 'so-called' G(T) function.
4467
4468 Given as a function of both temperature and eDensity.
4469
4470 Only the top( set by 'top') brightest lines are plotted."""
4471
4472
4473
4474
4475 if hasattr(self, 'Emiss'):
4476 doEmiss=False
4477 em=self.Emiss
4478 else:
4479 doEmiss=True
4480
4481
4482 if doEmiss:
4483
4484 self.emiss()
4485 em=self.Emiss
4486
4487
4488
4489
4490
4491
4492
4493
4494 fontsize=12
4495
4496 emiss=em["emiss"]
4497 wvl=em["wvl"]
4498 temperature=self.Temperature
4499 eDensity=self.EDensity
4500 plotLabels=em["plotLabels"]
4501 xLabel=plotLabels["xLabel"]
4502 yLabel=plotLabels["yLabel"]
4503
4504
4505
4506
4507 if not isinstance(wvlRange, int):
4508 igvl=util.between(wvl,wvlRange)
4509 else:
4510 igvl=range(len(wvl))
4511 nlines=len(igvl)
4512 if nlines < 2:
4513 print(' less than 2 lines in selected interval')
4514 self.message = ' less than 2 lines in selected interval'
4515 self.Error = 1
4516
4517
4518
4519
4520
4521 return
4522 self.Error = 0
4523
4524
4525 if (top > nlines) or (top == 0):
4526 top=nlines
4527 maxEmiss=np.zeros(nlines,'Float64')
4528 for iline in range(nlines):
4529 maxEmiss[iline]=emiss[igvl[iline]].max()
4530 for iline in range(nlines):
4531 if maxEmiss[iline]>=maxEmiss.max():
4532 maxAll=emiss[igvl[iline]]
4533 maxIndex = igvl[iline]
4534 print(' maxIndex, maxAll = %5i %5i '%(maxIndex, maxAll))
4535
4536 igvlsort=np.take(igvl,np.argsort(maxEmiss))
4537 topLines=igvlsort[-top:]
4538 maxWvl='%5.3f' % wvl[topLines[-1]]
4539
4540
4541
4542 good = np.where(emiss > 0.)
4543 emissMin=emiss[good].min()
4544 bad=np.where(emiss <= 0.)
4545 emiss[bad]=emissMin
4546
4547 topLines=topLines[wvl[topLines].argsort()]
4548
4549
4550 ntemp=self.Temperature.size
4551
4552 ndens=self.EDensity.size
4553
4554 ylabel = 'Emissivity relative to '+maxWvl
4555 title = self.Spectroscopic
4556
4557
4558 if ndens==1 and ntemp==1:
4559 print(' only a single temperature and eDensity')
4560 return
4561 elif ndens == 1:
4562 xlabel='Temperature (K)'
4563 ngofnt = temperature.size
4564 xvalues=temperature
4565 outTemperature=temperature
4566 outDensity=np.zeros(ntemp,'Float64')
4567 outDensity.fill(eDensity)
4568 desc_str=' at Density = %10.2e' % eDensity
4569 elif ntemp == 1:
4570 xvalues=eDensity
4571 ngofnt = eDensity.size
4572 outTemperature=np.zeros(ndens,'Float64')
4573 outTemperature.fill(temperature)
4574 outDensity=eDensity
4575 xlabel=r'$\rm{Electron Density (cm}^{-3}\rm{)}$'
4576 desc_str=' at Temperature = %10.2e' % temperature
4577 else:
4578 outTemperature=temperature
4579 outDensity=eDensity
4580 xlabel='Temperature (K)'
4581 xvalues=temperature
4582
4583 desc_str=' for variable Density'
4584
4585
4586
4587
4588
4589
4590 ymax = np.max(1.2*emiss[topLines[0]]/maxAll)
4591 ymin = ymax
4592 pl.figure()
4593 ax = pl.subplot(111)
4594 nxvalues=len(xvalues)
4595 for iline in range(top):
4596 tline=topLines[iline]
4597 pl.loglog(xvalues,emiss[tline]/maxAll)
4598 if np.min(emiss[tline]/maxAll) < ymin:
4599 ymin = np.min(emiss[tline]/maxAll)
4600 if np.max(emiss[tline]/maxAll) > ymax:
4601 ymax = np.max(emiss[tline]/maxAll)
4602 skip=2
4603 start=divmod(iline,nxvalues)[1]
4604 for ixvalue in range(start,nxvalues,nxvalues//skip):
4605 pl.text(xvalues[ixvalue],emiss[tline,ixvalue]/maxAll[ixvalue],str(wvl[tline]))
4606 pl.xlim(xvalues.min(),xvalues.max())
4607
4608
4609
4610
4611 pl.xlabel(xlabel,fontsize=fontsize)
4612 pl.ylabel(ylabel,fontsize=fontsize)
4613 if ndens == ntemp and ntemp > 1:
4614 pl.text(0.07, 0.5,title, horizontalalignment='left', verticalalignment='center', fontsize=fontsize, transform = ax.transAxes)
4615
4616 ax2 = pl.twiny()
4617 xlabelDen=r'Electron Density (cm$^{-3}$)'
4618 pl.xlabel(xlabelDen, fontsize=fontsize)
4619 pl.loglog(eDensity,emiss[topLines[top-1]]/maxAll, visible=False)
4620 ax2.xaxis.tick_top()
4621 pl.ylim(ymin/1.2, 1.2*ymax)
4622 else:
4623 pl.ylim(ymin/1.2, 1.2*ymax)
4624 pl.title(title+desc_str,fontsize=fontsize)
4625 if saveFile:
4626 pl.savefig(saveFile)
4627 else:
4628 pl.draw()
4629
4630
4631 wvlChoices = []
4632 for one in wvl[topLines]:
4633 wvlChoices.append('%12.3f'%(one))
4634 self.wvlChoices = wvlChoices
4635 self.topLines = topLines
4636
4637
4638
4640 """Plot the ratio of 2 lines or sums of lines.
4641
4642 Shown as a function of eDensity and/or temperature.
4643
4644 to save a plot or txt, only the directory name is needed"""
4645
4646
4647
4648
4649
4650 em = self.Emiss
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670 emiss = em['emiss']
4671 wvl = em["wvl"]
4672 plotLabels=em["plotLabels"]
4673 xLabel=plotLabels["xLabel"]
4674 yLabel=plotLabels["yLabel"]
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704 topLines = self.topLines
4705 maxWvl='%5.3f' % wvl[topLines[-1]]
4706
4707
4708
4709
4710
4711 good = np.where(emiss > 0.)
4712 emissMin=emiss[good].min()
4713 bad=np.where(emiss <= 0.)
4714 emiss[bad]=emissMin
4715
4716
4717 ntemp=self.Temperature.size
4718
4719 ndens=self.EDensity.size
4720
4721 ylabel='Emissivity relative to '+maxWvl
4722 title=self.Spectroscopic
4723
4724
4725 if ndens==1 and ntemp==1:
4726 print(' only a single temperature and eDensity')
4727 return
4728 elif ndens == 1:
4729 xlabel='Temperature (K)'
4730 xvalues=self.Temperature
4731 outTemperature=self.Temperature
4732 outDensity=np.zeros(ntemp,'Float64')
4733 outDensity.fill(self.EDensity)
4734 desc_str=' at Density = %10.2e (cm)$^{-3}$' % self.EDensity
4735 elif ntemp == 1:
4736 xvalues=self.EDensity
4737 outTemperature=np.zeros(ndens,'Float64')
4738 outTemperature.fill(self.Temperature)
4739 outDensity=self.EDensity
4740 xlabel=r'$\rm{Electron Density (cm)^{-3}}$'
4741 desc_str=' at Temp = %10.2e (K)' % self.Temperature
4742 else:
4743 outTemperature=self.Temperature
4744 outDensity=self.EDensity
4745 xlabel='Temperature (K)'
4746 xvalues=self.Temperature
4747 desc_str=' for variable Density'
4748
4749
4750
4751
4752
4753
4754
4755 if np.iterable(numIdx):
4756 num_idx=numIdx
4757 else:
4758 num_idx = [numIdx]
4759 if len(num_idx) == 0:
4760 print(' no numerator lines were selected')
4761 self.Message = ' no numerator lines were selected'
4762
4763
4764
4765
4766 return
4767
4768 if np.iterable(denIdx):
4769 den_idx=denIdx
4770 else:
4771 den_idx = [denIdx]
4772
4773 if len(den_idx) == 0:
4774 print(' no denominator lines were selected')
4775 self.Message = ' no denominator lines were selected'
4776 return
4777
4778
4779
4780
4781
4782
4783
4784 numEmiss=np.zeros(len(xvalues),'Float64')
4785 for aline in num_idx:
4786 numEmiss+=emiss[topLines[aline]]
4787
4788 denEmiss=np.zeros(len(xvalues),'Float64')
4789 for aline in den_idx:
4790 denEmiss+=emiss[topLines[aline]]
4791
4792 intRatio = numEmiss/denEmiss
4793 fontsize = 12
4794
4795
4796
4797 pl.figure()
4798 ax = pl.subplot(111)
4799 pl.loglog(xvalues,numEmiss/denEmiss)
4800 pl.xlim(xvalues.min(),xvalues.max())
4801 pl.xlabel(xlabel,fontsize=fontsize)
4802 pl.ylabel('Ratio ('+self.Defaults['flux']+')',fontsize=fontsize)
4803 desc = title + ':'
4804 for aline in num_idx:
4805 desc += ' ' + str(wvl[topLines[aline]])
4806 desc +=' / '
4807 for aline in den_idx:
4808 desc += ' ' + str(wvl[topLines[aline]])
4809 if ndens == ntemp and ntemp > 1:
4810 pl.text(0.07, 0.5,desc, horizontalalignment='left', verticalalignment='center', fontsize=fontsize, transform = ax.transAxes)
4811
4812 ax2 = pl.twiny()
4813 xlabelDen=r'Electron Density (cm$^{-3}$)'
4814 pl.xlabel(xlabelDen, fontsize=fontsize)
4815 pl.loglog(outDensity,intRatio, visible=False)
4816 ax2.xaxis.tick_top()
4817 else:
4818
4819 pl.title(desc,fontsize=fontsize)
4820
4821
4822
4823
4824
4825
4826
4827 intensityRatioFileName = self.IonStr
4828 for aline in num_idx:
4829 addstr = '%10.3f'%(wvl[topLines[aline]])
4830 intensityRatioFileName += '_' + addstr.strip()
4831 intensityRatioFileName+='_2'
4832 for aline in den_idx:
4833 addstr = '%10.3f'%(wvl[topLines[aline]])
4834 intensityRatioFileName += '_' + addstr.strip()
4835
4836
4837 self.IntensityRatio = {'ratio':intRatio,'desc':desc,
4838 'temperature':outTemperature,'eDensity':outDensity,'filename':intensityRatioFileName}
4839
4840 if plotDir:
4841 plotFile = os.path.join(plotDir, intensityRatioFileName+'.png')
4842 pl.savefig(plotFile)
4843
4844 if saveDir:
4845 txtFile = os.path.join(saveDir, intensityRatioFileName+'.txt')
4846 self.intensityRatioSave(outFile = txtFile)
4847
4848
4849
4851 '''
4852 Reads, calculates, and/or plots the ionization equilibrium for an element as a function of temperature.
4853 The variable z is the atomic number of the element. Acceptable values are from 1 to 30.
4854 '''
4856 '''
4857 a class to calculate and plot ionization equilibria
4858 '''
4859 self.Z=z
4860
4861
4862
4863 - def load(self, ioneqName):
4864 '''
4865 read in an existing file ionization equilibrium file
4866 ioneqName should be something like 'chianti', or 'chianti_version6'
4867 '''
4868 ioneqAll = io.ioneqRead(ioneqName)
4869 self.Temperature = ioneqAll['ioneqTemperature']
4870 self.Ioneq = ioneqAll['ioneqAll'][self.Z - 1]
4871
4872
4873
4875 '''
4876 returns with the ionization balance as self.Ioneq and
4877 temperature as self.Temperature
4878 '''
4879 self.Temperature = np.array(temperature, 'float64')
4880 ionList=[]
4881 chIons=[]
4882 z = self.Z
4883 for stage in range(1, z+2):
4884 ionStr=util.zion2name(z, stage)
4885 ionList.append(ionStr)
4886 print(' Z %5i Stage %5i ionStr %s'%(z, stage, ionStr))
4887 atom=ion(ionStr, temperature = self.Temperature)
4888 atom.ionizRate()
4889 atom.recombRate()
4890 chIons.append(atom)
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901 ntemp=chIons[0].IonizRate['temperature'].size
4902 if ntemp == 1:
4903 ioneq=np.zeros((z+1), 'Float64')
4904 factor = []
4905 for anIon in chIons:
4906 if hasattr(anIon, 'RecombRate') and hasattr(anIon, 'IonizRate'):
4907 rat=anIon.IonizRate['rate']/anIon.RecombRate['rate']
4908 factor.append(rat**2 + rat**(-2))
4909 else:
4910 factor.append(0.)
4911 factor[0]=max(factor)
4912 factor[-1]=max(factor)
4913 ionmax=factor.index(min(factor))
4914
4915 ioneq[ionmax]=1.
4916
4917 for iz in range(ionmax+1, z+1):
4918 ionrate=chIons[iz-1].IonizRate['rate']
4919 recrate=chIons[iz].RecombRate['rate']
4920 ioneq[iz]=ionrate*ioneq[iz-1]/recrate
4921
4922 for iz in range(ionmax-1, -1, -1):
4923 ionrate=chIons[iz].IonizRate['rate']
4924 recrate=chIons[iz+1].RecombRate['rate']
4925 ioneq[iz]=recrate*ioneq[iz+1]/ionrate
4926 ionsum=ioneq.sum()
4927
4928 ioneq=ioneq/ionsum
4929 self.Ioneq=ioneq
4930
4931 else:
4932 ioneq=np.zeros((z+1,ntemp ), 'Float64')
4933 for it in range(ntemp):
4934 factor=[]
4935 for anIon in chIons:
4936 if type(anIon.IonizRate) != type(None) and type(anIon.RecombRate) != type(None):
4937 rat=anIon.IonizRate['rate'][it]/anIon.RecombRate['rate'][it]
4938 factor.append(rat**2 + rat**(-2))
4939 else:
4940 factor.append(0.)
4941 factor[0]=max(factor)
4942 factor[-1]=max(factor)
4943 ionmax=factor.index(min(factor))
4944
4945 ioneq[ionmax, it]=1.
4946
4947 for iz in range(ionmax+1, z+1):
4948 ionrate=chIons[iz-1].IonizRate['rate'][it]
4949 recrate=chIons[iz].RecombRate['rate'][it]
4950 ioneq[iz, it]=ionrate*ioneq[iz-1, it]/recrate
4951
4952 for iz in range(ionmax-1, -1, -1):
4953 ionrate=chIons[iz].IonizRate['rate'][it]
4954 recrate=chIons[iz+1].RecombRate['rate'][it]
4955 ioneq[iz, it]=recrate*ioneq[iz+1, it]/ionrate
4956 ionsum=ioneq[:, it].sum()
4957
4958 ioneq[:, it]=ioneq[:, it]/ionsum
4959 self.Ioneq=ioneq
4960
4961
4962
4963 - def plot(self, stages=0, xr=0, yr=0, oplot=0, label=1, title=1, bw=0, semilogx = 0):
4964 '''
4965 Plots the ionization equilibria.
4966
4967 self.plot(xr=None, yr=None, oplot=False)
4968 stages = sequence of ions to be plotted, neutral == 1, fully stripped == Z+1
4969 xr = temperature range, yr = ion fraction range
4970
4971 for overplotting:
4972 oplot="ioneqfilename" such as 'mazzotta'
4973 or if oplot=True or oplot=1 and a widget will come up so that a file can be selected.
4974 '''
4975 if bw:
4976 linestyle=['k-','k--', 'k-.', 'k:']
4977 pl.rcParams['font.size'] = 16.
4978 lw = 2
4979 else:
4980 linestyle=['b-','r--', 'g-.', 'm:']
4981 lw = 1
4982
4983 if not stages:
4984 stages=range(1, self.Z+2)
4985 elif min(stages) < 1 or max(stages) > self.Z+1:
4986 stages=range(1, self.Z+2)
4987 if not xr:
4988 xr=[self.Temperature.min(), self.Temperature.max()]
4989 if not yr:
4990 yr=[0.01, 1.1]
4991 xyr=list(xr)
4992 xyr.extend(list(yr))
4993
4994 iz=stages[0]
4995 if semilogx:
4996 pl.semilogx(self.Temperature, self.Ioneq[iz-1], linestyle[0], lw=lw)
4997 else:
4998 pl.loglog(self.Temperature, self.Ioneq[iz-1], linestyle[0], lw=lw)
4999 if label:
5000 idx=self.Ioneq[iz-1] == self.Ioneq[iz-1].max()
5001 if idx.sum() > 1:
5002 jdx=np.arange(len(idx))
5003 idx=jdx[idx].max()
5004 ann=const.Ionstage[iz-1]
5005 pl.annotate(ann, [self.Temperature[idx], 0.7*self.Ioneq[iz-1, idx]], ha='center')
5006 for iz in stages[1:]:
5007 if semilogx:
5008 pl.semilogx(self.Temperature, self.Ioneq[iz-1], linestyle[0], lw=lw)
5009 else:
5010 pl.loglog(self.Temperature, self.Ioneq[iz-1], linestyle[0], lw=lw)
5011 if label:
5012 idx=self.Ioneq[iz-1] == self.Ioneq[iz-1].max()
5013 if idx.sum() > 1:
5014 jdx=np.arange(len(idx))
5015 idx=jdx[idx].mean()
5016 ann=const.Ionstage[iz-1]
5017 pl.annotate(ann, [self.Temperature[idx], 0.7*self.Ioneq[iz-1, idx]], ha='center')
5018 pl.xlabel('Temperature (K)')
5019 pl.ylabel('Ion Fraction')
5020 atitle='Chianti Ionization Equilibrium for '+const.El[self.Z-1].capitalize()
5021
5022 if oplot:
5023 if oplot == 0:
5024 result=io.ioneqRead(ioneqname='')
5025
5026 if result != False:
5027 atitle+=' & '+result['ioneqname'].replace('.ioneq', '')
5028 atitle+=' '+linestyle[0]
5029 for iz in stages:
5030 pl.plot(result['ioneqTemperature'], result['ioneqAll'][self.Z-1, iz-1],linestyle[1], lw=lw)
5031 elif type(oplot) == type('string'):
5032 atitle+=' & ' + oplot
5033 result = io.ioneqRead(ioneqname=oplot)
5034
5035
5036 if result != False:
5037 for iz in stages:
5038 pl.plot(result['ioneqTemperature'], result['ioneqAll'][self.Z-1, iz-1],linestyle[1], lw=lw)
5039 elif type(oplot) == type([]):
5040 for iplot in range(len(oplot)):
5041 result = io.ioneqRead(ioneqname=oplot[iplot])
5042
5043 if result != False:
5044 atitle+=' & '+oplot[iplot]+' '+linestyle[iplot%3]
5045 for iz in stages:
5046 pl.plot(result['ioneqTemperature'], result['ioneqAll'][self.Z-1, iz-1],linestyle[1], lw=lw)
5047 else:
5048 print(' oplot file not understood %s'%(oplot))
5049 if title:
5050 pl.title(atitle)
5051 pl.axis(xyr)
5052
5053
5054
5055
5056
5057