1
2 import numpy as np
3 import chianti.core as ch
4 from chianti import util
6 - def __init__(self,z, temperature, verbose=False):
7 '''calculates the ionization equilibrium for element z at a single temperatures'''
8
9 self.Z=z
10 self.Temperature=temperature
11
13 ''' get the ionization equilibrium for this ion at the specified temperature
14 getIoneqFromEvolver does the same but is a bit less accurate'''
15 z = self.Z
16 temperature = self.Temperature
17 ionList=[]
18 chIons=[]
19 for ion in range(1, z+2):
20 ions=util.zion2name(z, ion)
21 ionList.append(ions)
22
23 atom=ch.ion(ions, temperature=temperature, setup=0)
24 atom.ionizRate()
25 atom.recombRate()
26 chIons.append(atom)
27 ioneq=np.zeros((z+1), 'float64')
28 factor=[]
29 for anIon in chIons:
30 if type(anIon.IonizRate) != type(None) and type(anIon.RecombRate) != type(None):
31 rat=anIon.IonizRate['rate']/anIon.RecombRate['rate']
32 factor.append(rat**2 + rat**(-2))
33 else:
34 factor.append(0.)
35 factor[0]=max(factor)
36 factor[-1]=max(factor)
37 ionmax=factor.index(min(factor))
38 ioneq[ionmax]=1.
39
40 for iz in range(ionmax+1, z+1):
41 ionrate=chIons[iz-1].IonizRate['rate']
42 recrate=chIons[iz].RecombRate['rate']
43 ioneq[iz]=ionrate*ioneq[iz-1]/recrate
44
45
46 for iz in range(ionmax-1, -1, -1):
47 ionrate=chIons[iz].IonizRate['rate']
48 recrate=chIons[iz+1].RecombRate['rate']
49 ioneq[iz]=recrate*ioneq[iz+1]/ionrate
50
51 ionsum=ioneq.sum()
52
53 ioneq=ioneq/ionsum
54 self.Ioneq=ioneq
55
56
57
59 '''gets the ionization equilibrium for the specified ion at the specified temperature
60 not as accurate as getIoneq since this methods uses a matrix inversion which can result in
61 nonphysical values (usually small) that are zero'd out '''
62 z = self.Z
63 self.getEvolver(self.Temperature)
64 b = np.zeros((z+1),'float64')
65 b[0] = 1.
66 static = self.Static
67 ioneq = np.linalg.solve(evolver,b)
68 ioneq = np.where(ioneq >= 0., ioneq, 0.)
69 self.Ioneq = ioneq
70
71
72
73
75 ''' calculate the static matrix for the specified temperature'''
76 temperature = self.Temperature
77 z = self.Z
78 ionList=[]
79 chIons=[]
80 evolver = np.zeros((z+1,z+1),'float64')
81 for ion in range(1, z+2):
82 ions=util.zion2name(z, ion)
83 ionList.append(ions)
84
85 atom=ch.ion(ions, temperature=temperature)
86 atom.ionizRate()
87 atom.recombRate()
88 chIons.append(atom)
89 for stage in range(0,z):
90 atom = chIons[stage]
91
92 evolver[stage,stage] -= atom.IonizRate['rate']
93 atom = chIons[stage+1]
94 evolver[stage,stage+1] += atom.RecombRate['rate']
95
96 for stage in range(1,z+1):
97 atom = chIons[stage]
98
99 evolver[stage,stage] -= atom.RecombRate['rate']
100 atom = chIons[stage-1]
101 evolver[stage,stage-1] += atom.IonizRate['rate']
102
103
104 for stage in range(0,z+1):
105 evolver[0,stage] = 1.
106 self.Static = evolver
107
108
109
111 ''' calculate the evolver matrix for the new temperature'''
112 self.NewTemperature = temperature
113 z = self.Z
114 ionList=[]
115 chIons=[]
116 evolver = np.zeros((z+1,z+1),'float64')
117 for ion in range(1, z+2):
118 ions=util.zion2name(z, ion)
119 ionList.append(ions)
120
121 atom=ch.ion(ions, temperature=temperature, setup=0)
122 atom.ionizRate()
123 atom.recombRate()
124 chIons.append(atom)
125 for stage in range(0,z):
126
127
128 evolver[stage,stage] -= chIons[stage].IonizRate['rate']
129
130 evolver[stage,stage+1] += chIons[stage+1].RecombRate['rate']
131
132 for stage in range(1,z+1):
133
134
135 evolver[stage,stage] -= chIons[stage].RecombRate['rate']
136
137 evolver[stage,stage-1] += chIons[stage-1].IonizRate['rate']
138
139
140
141
142
143
144 implicitEvolver = np.dual.inv(np.identity(z+1,'float64') - density*time*evolver)
145 self.Evolver = implicitEvolver
146
147
148
149
150
151
152 - def evolve(self,temperature, density, haveEvolver = 0):
153 ''' to evolve the plasma to the final temperature'''
154 z = self.Z
155 self.finalTemperature=temperature
156 if not haveEvolver:
157 self.getEvolver(temperature,density,1.)
158
159
160
161 maxDiff = 1.
162
163
164
165 new = np.dot(self.Evolver,self.Ioneq)
166 diff = np.where(self.Ioneq > 0.,np.abs(new - self.Ioneq)/self.Ioneq,100.)
167 print((' diff = %10.2e'%(diff)))
168 dt = maxDiff/diff.min()
169 print((' dt = %12.2e'%(dt)))
170 self.getEvolver(temperature,density,dt)
171 niter = int(10.*maxDiff/diff.min())+1
172 print((' niter = %i'%(niter)))
173 state = np.zeros((niter+1,z+1),'float64')
174 stateTot = np.zeros(niter+1,'float64')
175 stateDiff = np.zeros((niter,z+1,z+1),'float64')
176 change = np.zeros((niter+1),'float64')
177 deltaChange = np.zeros((niter),'float64')
178 deltaChange[0] = 1.
179 state[0] = self.Ioneq
180 stateTot[0] = self.Ioneq.sum()
181 previousChange = 1.
182 dChange = 10.
183 iter = 0
184
185 while (dChange > 1.e-7) and (iter < niter):
186 present = state[iter]
187
188
189
190
191
192 new = np.dot(self.Evolver,present)
193 thisdiff = np.where(present > 0.,np.abs(new - present)/present,0.)
194
195 state[iter+1] = new
196 stateTot[iter+1] = new.sum()
197
198 change[iter] = thisdiff.sum()
199 deltaChange[iter] = np.abs(change[iter] - previousChange)
200 dChange = deltaChange[iter]
201 previousChange = change[iter]
202 iter+=1
203 print((' # of iterations = %i'%(iter)))
204 self.Temperature = self.finalTemperature
205 self.getIoneq()
206 state[iter] = self.Ioneq
207 self.State = state[:iter].transpose()
208 self.StateTot = stateTot[:iter]
209 self.StateDiff = stateDiff[:iter]
210 self.Change = change[:iter]
211 self.DeltaChange = deltaChange[:iter]
212 self.Iter = iter
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293