33 integer,
parameter :: n_Tonly = 100
34 double precision :: HI_fraction_chromosphere(n_Tonly)
35 double precision :: T_H_fraction_K(n_Tonly)
36 double precision :: ionization_He_abundance = 0.1d0
37 double precision :: ionization_Rfactor_norm = 2.3d0
38 double precision,
allocatable :: prom_T_table(:)
39 double precision,
allocatable :: prom_p_table(:)
40 double precision,
allocatable :: prom_T_eos_table(:)
41 double precision,
allocatable :: prom_Y_eos_table(:,:)
42 double precision :: Te_table_min, Te_table_max, inv_Te_table_step
43 double precision :: Te_low_iz_He=5413.d0
44 double precision :: Y_table_min, Y_table_max
45 double precision :: ionization_H_energy_unit = 0.d0
51 double precision :: ionization_HeI_energy_unit = 0.d0
52 double precision :: ionization_HeII_energy_unit = 0.d0
53 double precision,
dimension(:),
allocatable :: Rfactor_table
54 double precision,
dimension(:),
allocatable :: eps_ion_H_table
55 double precision,
dimension(:),
allocatable :: Te_H_table
56 double precision,
dimension(:),
allocatable :: iz_H_table
58 double precision,
dimension(:),
allocatable :: Y_table
61 integer,
parameter :: ionization_mode_chromosphere = 1
66 integer,
parameter :: ionization_mode_prominence = 2
71 integer,
parameter :: ionization_mode_flare = 3
72 integer,
parameter :: n_prom_T = 10
73 integer,
parameter :: n_prom_p = 7
74 integer,
parameter :: n_prom_eos = 4000
75 integer :: ionization_table_mode = ionization_mode_chromosphere
97 logical :: ionization_initialized = .false.
98 logical :: ionization_include_energy = .false.
101 logical :: pi_newton_warned = .false.
103 double precision,
parameter :: prom_T_K(n_prom_T) = (/ &
104 6000.d0, 7000.d0, 8000.d0, 9000.d0, 10000.d0, &
105 12000.d0, 15000.d0, 20000.d0, 25000.d0, 30000.d0 /)
107 double precision,
parameter :: prom_p_cgs(n_prom_p) = (/ &
108 0.03d0, 0.05d0, 0.10d0, 0.30d0, 0.50d0, 0.70d0, 1.00d0 /)
112 double precision,
parameter :: &
113 prom_iz_H_data(n_prom_T,n_prom_p) = reshape((/ &
115 0.53d0, 0.59d0, 0.65d0, 0.73d0, 0.80d0, &
116 0.90d0, 0.95d0, 0.98d0, 0.99d0, 1.00d0, &
118 0.40d0, 0.47d0, 0.56d0, 0.69d0, 0.79d0, &
119 0.91d0, 0.96d0, 0.98d0, 0.99d0, 1.00d0, &
121 0.25d0, 0.32d0, 0.47d0, 0.67d0, 0.81d0, &
122 0.93d0, 0.97d0, 0.99d0, 0.99d0, 1.00d0, &
124 0.10d0, 0.17d0, 0.41d0, 0.69d0, 0.85d0, &
125 0.96d0, 0.98d0, 0.99d0, 1.00d0, 1.00d0, &
127 0.06d0, 0.14d0, 0.38d0, 0.70d0, 0.87d0, &
128 0.97d0, 0.99d0, 1.00d0, 1.00d0, 1.00d0, &
130 0.04d0, 0.12d0, 0.36d0, 0.70d0, 0.88d0, &
131 0.97d0, 0.99d0, 1.00d0, 1.00d0, 1.00d0, &
133 0.03d0, 0.10d0, 0.34d0, 0.71d0, 0.89d0, &
134 0.98d0, 0.99d0, 1.00d0, 1.00d0, 1.00d0 /), &
135 (/ n_prom_t, n_prom_p /))
140 double precision,
parameter :: HI_fraction_flare(n_Tonly) = (/ &
141 9.9999877d-1, 9.9999877d-1, 9.9999877d-1, 9.9999877d-1, 9.9999877d-1, &
142 9.9999877d-1, 9.9999877d-1, 9.9999877d-1, 9.9999877d-1, 9.9999877d-1, &
143 9.9999877d-1, 9.9999890d-1, 9.9998024d-1, 9.9992560d-1, 9.9968205d-1, &
144 9.9899492d-1, 9.9682125d-1, 9.9069732d-1, 9.8273722d-1, 9.5165553d-1, &
145 8.8401513d-1, 7.5808722d-1, 6.3272201d-1, 4.7164997d-1, 1.6497967d-1, &
146 8.6151552d-2, 4.9787498d-2, 2.3629016d-2, 1.1010071d-2, 7.7663095d-3, &
147 5.7739222d-3, 4.1811404d-3, 3.4427999d-3, 2.9108183d-3, 2.2295092d-3, &
148 1.9451997d-3, 1.4754925d-3, 1.1849689d-3, 9.1381928d-4, 7.1369446d-4, &
149 6.3154203d-4, 5.4204957d-4, 4.6800799d-4, 4.1090740d-4, 3.4051796d-4, &
150 2.9397681d-4, 2.5497294d-4, 2.0900410d-4, 1.7267453d-4, 1.3474650d-4, &
151 1.0894726d-4, 8.6230407d-5, 7.0545733d-5, 5.8882317d-5, 4.9311732d-5, &
152 3.9687148d-5, 3.1954017d-5, 2.6460585d-5, 2.1367807d-5, 1.7836169d-5, &
153 1.4207015d-5, 1.2022741d-5, 9.9158901d-6, 8.5561393d-6, 7.1994135d-6, &
154 6.1326905d-6, 5.0944881d-6, 4.3131760d-6, 3.6080551d-6, 3.0571391d-6, &
155 2.5778758d-6, 2.1649433d-6, 1.7960119d-6, 1.4711565d-6, 1.1832833d-6, &
156 9.3858978d-7, 7.4227325d-7, 5.8536296d-7, 4.6590828d-7, 3.8339449d-7, &
157 3.2851748d-7, 2.8778528d-7, 2.5213506d-7, 2.2042234d-7, 1.9440791d-7, &
158 1.7116454d-7, 1.5143655d-7, 1.3390303d-7, 1.1879927d-7, 1.0569364d-7, &
159 9.4140342d-8, 8.3767251d-8, 7.4822295d-8, 6.6863923d-8, 5.9836421d-8, &
160 5.3685738d-8, 4.8177839d-8, 4.3423310d-8, 3.9109303d-8, 1.0188512d-8 /)
164 data t_h_fraction_k /2.1000005d+03, 2.2349495d+03, 2.3785708d+03, 2.5314199d+03, 2.6940928d+03, &
165 2.8672190d+03, 3.0514692d+03, 3.2475613d+03, 3.4562544d+03, 3.6783584d+03, &
166 3.9147351d+03, 4.1662998d+03, 4.4340322d+03, 4.7189697d+03, 5.0222153d+03, &
167 5.3449502d+03, 5.6884248d+03, 6.0539712d+03, 6.4430083d+03, 6.8570420d+03, &
168 7.2976860d+03, 7.7666460d+03, 8.2657383d+03, 8.7969062d+03, 9.3622090d+03, &
169 9.9638330d+03, 1.0604130d+04, 1.1285561d+04, 1.2010781d+04, 1.2782619d+04, &
170 1.3604041d+04, 1.4478250d+04, 1.5408652d+04, 1.6398826d+04, 1.7452648d+04, &
171 1.8574172d+04, 1.9767766d+04, 2.1038082d+04, 2.2390010d+04, 2.3828838d+04, &
172 2.5360100d+04, 2.6989764d+04, 2.8724182d+04, 3.0570023d+04, 3.2534518d+04, &
173 3.4625215d+04, 3.6850262d+04, 3.9218293d+04, 4.1738543d+04, 4.4420699d+04, &
174 4.7275266d+04, 5.0313219d+04, 5.3546391d+04, 5.6987395d+04, 6.0649453d+04, &
175 6.4546914d+04, 6.8694758d+04, 7.3109148d+04, 7.7807289d+04, 8.2807258d+04, &
176 8.8128625d+04, 9.3791852d+04, 9.9819000d+04, 1.0623346d+05, 1.1306024d+05, &
177 1.2032560d+05, 1.2805797d+05, 1.3628709d+05, 1.4504503d+05, 1.5436592d+05, &
178 1.6428561d+05, 1.7484295d+05, 1.8607852d+05, 1.9803609d+05, 2.1076208d+05, &
179 2.2430608d+05, 2.3872045d+05, 2.5406084d+05, 2.7038703d+05, 2.8776234d+05, &
180 3.0625456d+05, 3.2593475d+05, 3.4688000d+05, 3.6917081d+05, 3.9289406d+05, &
181 4.1814181d+05, 4.4501247d+05, 4.7360988d+05, 5.0404450d+05, 5.3643488d+05, &
182 5.7090725d+05, 6.0759431d+05, 6.4663956d+05, 6.8819319d+05, 7.3241712d+05, &
183 7.7948294d+05, 8.2957412d+05, 8.8288331d+05, 9.3961919d+05, 1.0000000d+06 /
187 data hi_fraction_chromosphere /1.0000000d+00, 1.0000000d+00, 1.0000000d+00, 1.0000000d+00, 1.0000000d+00, &
188 1.0000000d+00, 1.0000000d+00, 9.9999928d-01, 9.9999774d-01, 9.9999624d-01, &
189 9.9999428d-01, 9.9999219d-01, 9.9998862d-01, 9.9998313d-01, 9.9997944d-01, &
190 9.9995774d-01, 1.0000324d+00, 9.9822283d-01, 9.6391600d-01, 8.8995951d-01, &
191 8.0025935d-01, 7.4219799d-01, 7.1378660d-01, 6.9565988d-01, 6.7326778d-01, &
192 6.5032905d-01, 6.2487972d-01, 5.9925246d-01, 5.7028764d-01, 5.3673893d-01, &
193 4.9144468d-01, 4.4332290d-01, 3.9158964d-01, 3.4621361d-01, 2.7826238d-01, &
194 1.9633104d-01, 1.2593637d-01, 8.1383914d-02, 5.0944358d-02, 2.8770180d-02, &
195 1.4449068d-02, 7.6071853d-03, 4.3617180d-03, 2.7694483d-03, 1.8341533d-03, &
196 1.2467797d-03, 8.6159195d-04, 6.0232455d-04, 4.2862241d-04, 3.0666622d-04, &
197 2.2256430d-04, 1.6328457d-04, 1.2179965d-04, 9.2065704d-05, 7.0357783d-05, &
198 5.4168402d-05, 4.2026968d-05, 3.2807184d-05, 2.5801779d-05, 2.0414085d-05, &
199 1.6271379d-05, 1.3041737d-05, 1.0538992d-05, 8.5329248d-06, 6.9645471d-06, &
200 5.7069005d-06, 4.7155136d-06, 3.9203474d-06, 3.2734958d-06, 2.7415674d-06, &
201 2.2911979d-06, 1.9045801d-06, 1.5692771d-06, 1.2782705d-06, 1.0281080d-06, &
202 8.1567373d-07, 6.4200310d-07, 5.0668103d-07, 4.0577518d-07, 3.3245340d-07, &
203 2.8006056d-07, 2.4119515d-07, 2.1033340d-07, 1.8429903d-07, 1.6189058d-07, &
204 1.4264889d-07, 1.2616209d-07, 1.1193421d-07, 9.9408616d-08, 8.8335305d-08, &
205 7.8593857d-08, 7.0042269d-08, 6.2532941d-08, 5.5898361d-08, 5.0049657d-08, &
206 4.4888207d-08, 4.0317172d-08, 3.6192560d-08, 3.2330732d-08, 2.9387850d-08 /
215 double precision,
intent(in) :: he_abundance, rfactor_norm
216 character(len=*),
intent(in),
optional :: table_name
217 character(len=32) :: selected_table
218 logical,
intent(in),
optional :: include_energy
219 double precision,
parameter :: erg_to_joule = 1.0
d-7
220 double precision :: energy_unit
222 if (ionization_initialized)
then
223 call mpistop(
"ionization_degree_init called more than once")
226 if (he_abundance < 0.d0)
call mpistop(
"negative He abundance")
227 if (rfactor_norm <= 0.d0)
call mpistop(
"invalid Rfactor normalization")
229 ionization_he_abundance = he_abundance
230 ionization_rfactor_norm = rfactor_norm
232 ionization_include_energy = .false.
233 if (
present(include_energy)) ionization_include_energy = include_energy
235 selected_table =
"chromosphere"
236 if (
present(table_name)) selected_table = trim(adjustl(table_name))
238 select case (trim(selected_table))
239 case (
"chromosphere")
240 ionization_table_mode = ionization_mode_chromosphere
242 ionization_table_mode = ionization_mode_flare
244 ionization_table_mode = ionization_mode_prominence
247 "Unknown ionization table. Use 'chromosphere', 'flare', "// &
251 if (ionization_include_energy .and. &
253 call mpistop(
"Ionization energy is supported only for T-only tables "// &
254 "('chromosphere' and 'flare'), not for the pressure-dependent "// &
255 "'prominence' table.")
258 if (ionization_include_energy)
then
260 call mpistop(
"invalid units for hydrogen ionization energy")
269 ionization_h_energy_unit = 13.6d0 * energy_unit
270 ionization_hei_energy_unit = 24.59d0 * energy_unit
271 ionization_heii_energy_unit = 54.42d0 * energy_unit
273 ionization_h_energy_unit = 0.d0
274 ionization_hei_energy_unit = 0.d0
275 ionization_heii_energy_unit = 0.d0
278 select case (ionization_table_mode)
279 case (ionization_mode_chromosphere)
280 call ionization_build_temperature_table(hi_fraction_chromosphere)
281 case (ionization_mode_flare)
282 call ionization_build_temperature_table(hi_fraction_flare)
283 case (ionization_mode_prominence)
284 call ionization_build_prominence_table()
286 ionization_initialized = .true.
289 subroutine ionization_build_temperature_table(HI_fraction)
292 double precision,
intent(in) :: hi_fraction(n_tonly)
293 double precision :: fact1, fact2, fact3
294 double precision :: dl1, dl2, te_table_step
295 double precision :: iz_h_source(n_tonly)
296 integer :: i, j, n_interpolated_table
299 n_interpolated_table=4000
302 iz_h_source=1.d0-hi_fraction
304 allocate(te_h_table(1:n_interpolated_table))
305 allocate(iz_h_table(1:n_interpolated_table))
309 te_table_step = (t_h_fraction_k(n_tonly)-t_h_fraction_k(1)) &
310 /dble(n_interpolated_table-1)
312 te_h_table(1) = t_h_fraction_k(1)
313 iz_h_table(1) = iz_h_source(1)
315 te_h_table(n_interpolated_table) = t_h_fraction_k(n_tonly)
316 iz_h_table(n_interpolated_table) = iz_h_source(n_tonly)
318 do i=2,n_interpolated_table
319 te_h_table(i) = te_h_table(i-1)+te_table_step
322 if(te_h_table(i) < t_h_fraction_k(j+1))
then
323 if(j.eq. n_tonly-1)
then
324 fact1 = (te_h_table(i)-t_h_fraction_k(j+1)) &
325 /(t_h_fraction_k(j)-t_h_fraction_k(j+1))
327 fact2 = (te_h_table(i)-t_h_fraction_k(j)) &
328 /(t_h_fraction_k(j+1)-t_h_fraction_k(j))
330 iz_h_table(i) = iz_h_source(j)*fact1 + &
331 iz_h_source(j+1)*fact2
334 dl1 = iz_h_source(j+1)-iz_h_source(j)
335 dl2 = iz_h_source(j+2)-iz_h_source(j+1)
336 jump = (max(dabs(dl1),dabs(dl2)) > &
337 2.d0*min(dabs(dl1),dabs(dl2)))
341 fact1 = (te_h_table(i)-t_h_fraction_k(j+1)) &
342 /(t_h_fraction_k(j)-t_h_fraction_k(j+1))
344 fact2 = (te_h_table(i)-t_h_fraction_k(j)) &
345 /(t_h_fraction_k(j+1)-t_h_fraction_k(j))
347 iz_h_table(i) = iz_h_source(j)*fact1 + &
348 iz_h_source(j+1)*fact2
351 fact1 = ((te_h_table(i)-t_h_fraction_k(j+1)) &
352 * (te_h_table(i)-t_h_fraction_k(j+2))) &
353 / ((t_h_fraction_k(j)-t_h_fraction_k(j+1)) &
354 * (t_h_fraction_k(j)-t_h_fraction_k(j+2)))
356 fact2 = ((te_h_table(i)-t_h_fraction_k(j)) &
357 * (te_h_table(i)-t_h_fraction_k(j+2))) &
358 / ((t_h_fraction_k(j+1)-t_h_fraction_k(j)) &
359 * (t_h_fraction_k(j+1)-t_h_fraction_k(j+2)))
361 fact3 = ((te_h_table(i)-t_h_fraction_k(j)) &
362 * (te_h_table(i)-t_h_fraction_k(j+1))) &
363 / ((t_h_fraction_k(j+2)-t_h_fraction_k(j)) &
364 * (t_h_fraction_k(j+2)-t_h_fraction_k(j+1)))
366 iz_h_table(i) = iz_h_source(j)*fact1 + &
367 iz_h_source(j+1)*fact2 + iz_h_source(j+2)*fact3
375 te_table_min=te_h_table(1)
376 te_table_max=te_h_table(n_interpolated_table)
377 inv_te_table_step = &
378 dble(n_interpolated_table-1)/(te_table_max-te_table_min)
380 call ionization_build_eos_table()
381 end subroutine ionization_build_temperature_table
383 subroutine ionization_build_prominence_table()
388 double precision :: pressure_unit_cgs
389 double precision ::
dt, rfactor
392 call mpistop(
"invalid units for prominence ionization table")
403 allocate(prom_t_table(n_prom_t))
404 allocate(prom_p_table(n_prom_p))
405 allocate(prom_t_eos_table(n_prom_eos))
406 allocate(prom_y_eos_table(n_prom_eos,n_prom_p))
409 prom_p_table = prom_p_cgs/pressure_unit_cgs
411 dt = (1.d6 - 2.1000005d3) / dble(n_prom_eos-1)
412 do it = 1, n_prom_eos
413 prom_t_eos_table(
it) = &
418 do it = 1, n_prom_eos
420 prom_y_eos_table(
it,ip) = &
421 rfactor*prom_t_eos_table(
it)
425 call ionization_check_prominence_y_table()
426 end subroutine ionization_build_prominence_table
428 subroutine ionization_prominence_pressure_bracket(p, ip, weight)
429 double precision,
intent(in) :: p
430 integer,
intent(out) :: ip
431 double precision,
intent(out) :: weight
434 double precision :: p_use
436 p_use = min(prom_p_table(n_prom_p), &
437 max(prom_p_table(1), p))
439 if (p_use <= prom_p_table(1))
then
443 else if (p_use >= prom_p_table(n_prom_p))
then
450 if (p_use <= prom_p_table(j+1))
then
452 weight = (p_use-prom_p_table(j)) / &
453 (prom_p_table(j+1)-prom_p_table(j))
457 end subroutine ionization_prominence_pressure_bracket
463 integer,
intent(in) :: ixi^
l, ixo^
l
464 double precision,
intent(in) :: te(ixi^s)
465 double precision,
intent(out) :: iz_h(ixo^s), iz_he(ixo^s)
467 double precision :: rtmp
469 {
do ix^db=ixomin^db,ixomax^db\}
471 te(ix^
d), rtmp, iz_h(ix^
d), iz_he(ix^
d))
475 subroutine ionization_h_degree_temperature_scalar(T, iz_H)
478 double precision,
intent(in) :: t
479 double precision,
intent(out) :: iz_h
483 if (.not.
allocated(te_h_table) .or. &
484 .not.
allocated(iz_h_table))
then
485 call mpistop(
"temperature-only ionization table is not initialized")
488 if (t < te_table_min)
then
490 else if (t >= te_table_max)
then
493 i = int((t-te_table_min)*inv_te_table_step) + 1
494 i = max(1, min(
size(te_h_table)-1, i))
496 iz_h = iz_h_table(i) + &
497 (t-te_h_table(i)) * &
498 (iz_h_table(i+1)-iz_h_table(i)) * &
502 iz_h = min(1.d0, max(0.d0, iz_h))
503 end subroutine ionization_h_degree_temperature_scalar
505 subroutine ionization_h_degree_prominence_scalar(T, p, iz_H)
508 double precision,
intent(in) :: t, p
509 double precision,
intent(out) :: iz_h
512 double precision :: weight_t, weight_p
513 double precision :: iz_low, iz_high
515 if (.not.
allocated(prom_t_table) .or. &
516 .not.
allocated(prom_p_table))
then
517 call mpistop(
"prominence ionization table is not initialized")
520 call ionization_prominence_pressure_bracket(p, ip, weight_p)
522 if (t <= prom_t_table(1))
then
525 else if (t >= prom_t_table(n_prom_t))
then
531 if (t <= prom_t_table(j+1))
then
537 weight_t = (t-prom_t_table(it)) / &
538 (prom_t_table(it+1)-prom_t_table(it))
541 if (it == 1 .and. weight_t == 0.d0)
then
542 iz_h = (1.d0-weight_p)*prom_iz_h_data(1,ip) + &
543 weight_p*prom_iz_h_data(1,ip+1)
545 iz_low = (1.d0-weight_p)*prom_iz_h_data(it,ip) + &
546 weight_p*prom_iz_h_data(it,ip+1)
548 iz_high = (1.d0-weight_p)*prom_iz_h_data(it+1,ip) + &
549 weight_p*prom_iz_h_data(it+1,ip+1)
551 iz_h = (1.d0-weight_t)*iz_low + weight_t*iz_high
554 iz_h = min(1.d0, max(0.d0, iz_h))
555 end subroutine ionization_h_degree_prominence_scalar
559 double precision,
intent(in) :: rho, p
560 double precision,
intent(out) :: t, rfactor
561 double precision,
intent(out),
optional :: iz_h, iz_he
563 double precision :: y
564 double precision :: iz_h_loc, iz_he_loc
566 if (.not. ionization_initialized)
then
567 call mpistop(
"ionization tables are not initialized")
570 if (rho <= 0.d0 .or. p <= 0.d0)
then
571 select case (ionization_table_mode)
572 case (ionization_mode_chromosphere, ionization_mode_flare)
574 case (ionization_mode_prominence)
575 t = prom_t_eos_table(1)
580 select case (ionization_table_mode)
581 case (ionization_mode_chromosphere, ionization_mode_flare)
582 call ionization_invert_y_scalar(y, t)
584 case (ionization_mode_prominence)
585 call ionization_invert_y_prominence_scalar(y, p, t)
588 call mpistop(
"invalid ionization table mode")
594 if (
present(iz_h)) iz_h = iz_h_loc
595 if (
present(iz_he)) iz_he = iz_he_loc
598 subroutine ionization_he_degree_scalar(T, iz_He)
601 double precision,
intent(in) :: t
602 double precision,
intent(out) :: iz_he
604 if (t < te_low_iz_he)
then
611 iz_he = min(1.d0, max(0.d0, iz_he))
612 end subroutine ionization_he_degree_scalar
617 double precision,
intent(in) :: t, p
618 double precision,
intent(out) :: rfactor
619 double precision,
intent(out),
optional :: iz_h, iz_he
621 double precision :: iz_h_loc, iz_he_loc
623 select case (ionization_table_mode)
624 case (ionization_mode_chromosphere, ionization_mode_flare)
625 call ionization_h_degree_temperature_scalar(t, iz_h_loc)
627 case (ionization_mode_prominence)
628 call ionization_h_degree_prominence_scalar( &
632 call mpistop(
"invalid ionization table mode")
635 call ionization_he_degree_scalar(t, iz_he_loc)
637 iz_h_loc = min(1.d0, max(0.d0, iz_h_loc))
638 iz_he_loc = min(1.d0, max(0.d0, iz_he_loc))
640 rfactor = ionization_rfactor_from_degrees( &
643 if (
present(iz_h)) iz_h = iz_h_loc
644 if (
present(iz_he)) iz_he = iz_he_loc
650 integer,
parameter :: max_iter = 80
651 double precision,
intent(in) :: rho, t
652 double precision,
intent(out) :: p, rfactor
655 double precision :: plo, phi, pmid
656 double precision :: rlo, rhi, rmid
657 double precision :: fhi, fmid, scale
659 if (.not. ionization_initialized)
then
660 call mpistop(
"ionization pressure solver called before initialization")
662 if (rho <= 0.d0 .or. t <= 0.d0)
then
663 call mpistop(
"invalid rho or T in ionization pressure solver")
673 plo = prom_p_table(1)
682 phi = prom_p_table(n_prom_p)
693 do iter = 1, max_iter
694 pmid = 0.5d0*(plo+phi)
696 fmid = pmid-rho*rmid*t
697 scale = max(abs(pmid), abs(rho*rmid*t), tiny(1.d0))
699 if (abs(fmid) <= 1.d-12*scale)
then
705 if (fmid > 0.d0)
then
712 call mpistop(
"pressure-dependent EOS solve did not converge")
716 integer,
intent(in) :: ixi^l, ixo^l
717 double precision,
intent(in) :: rho(ixi^s), p(ixi^s)
718 double precision,
intent(out) :: t(ixi^s), rfactor(ixi^s)
719 double precision,
intent(out),
optional :: iz_h(ixi^s), iz_he(ixi^s)
721 double precision :: iz_h_loc, iz_he_loc
724 {
do ix^db=ixomin^db,ixomax^db\}
726 rfactor(ix^d), iz_h_loc, iz_he_loc)
728 if (
present(iz_h)) iz_h(ix^d) = iz_h_loc
729 if (
present(iz_he)) iz_he(ix^d) = iz_he_loc
733 subroutine ionization_invert_y_scalar(y, T)
735 double precision,
intent(in) :: y
736 double precision,
intent(out) :: t
738 integer :: ilo, ihi, imid, n
739 double precision :: denom
741 if (.not.
allocated(y_table) .or. .not.
allocated(rfactor_table))
then
742 call mpistop(
"ionization_invert_y_scalar: EOS tables are not initialized")
748 call mpistop(
"ionization_invert_y_scalar: EOS table has fewer than 2 points")
756 if (y < y_table_min)
then
757 if (rfactor_table(1) > 0.d0)
then
758 t = max(tiny(1.d0), y/rfactor_table(1))
765 if (y >= y_table_max)
then
766 if (rfactor_table(n) > 0.d0)
then
767 t = y/rfactor_table(n)
777 do while (ihi - ilo > 1)
779 if (y >= y_table(imid))
then
786 denom = y_table(ilo+1) - y_table(ilo)
788 if (denom <= max(tiny(denom), 1.d-12*dabs(y_table(ilo))))
then
789 call mpistop(
"ionization_invert_y_scalar: non-increasing Y_table interval")
792 t = te_h_table(ilo) + (y - y_table(ilo)) * &
793 (te_h_table(ilo+1) - te_h_table(ilo))/denom
794 end subroutine ionization_invert_y_scalar
799 double precision,
intent(in) :: t
800 double precision,
intent(out) :: rfactor
801 double precision,
intent(out),
optional :: iz_h, iz_he
802 double precision :: iz_h_loc, iz_he_loc
805 call mpistop(
"temperature-only ionization state requested "// &
806 "for a pressure-dependent table")
809 select case (ionization_table_mode)
810 case (ionization_mode_chromosphere, ionization_mode_flare)
811 call ionization_h_degree_temperature_scalar(t, iz_h_loc)
813 call ionization_he_degree_scalar(t, iz_he_loc)
815 rfactor = ionization_rfactor_from_degrees( &
818 if (
present(iz_h)) iz_h = iz_h_loc
819 if (
present(iz_he)) iz_he = iz_he_loc
822 subroutine ionization_check_prominence_y_table()
827 do it = 1, n_prom_eos-1
828 if (prom_y_eos_table(it+1,ip) <= &
829 prom_y_eos_table(it,ip))
then
830 call mpistop(
"prominence EOS Y(T,p) is not monotonic")
834 end subroutine ionization_check_prominence_y_table
836 subroutine ionization_invert_y_prominence_scalar(y, p, T)
839 double precision,
intent(in) :: y, p
840 double precision,
intent(out) :: t
842 integer :: ip, ilo, ihi, imid
843 double precision :: wp, ylo, yhi, ymid
844 double precision :: ymin, ymax, rend, denom
846 if (.not.
allocated(prom_y_eos_table))
then
847 call mpistop(
"prominence EOS inversion table is missing")
850 call ionization_prominence_pressure_bracket(p, ip, wp)
852 ymin = (1.d0-wp)*prom_y_eos_table(1,ip) + &
853 wp*prom_y_eos_table(1,ip+1)
855 ymax = (1.d0-wp)*prom_y_eos_table(n_prom_eos,ip) + &
856 wp*prom_y_eos_table(n_prom_eos,ip+1)
859 t = prom_t_eos_table(1)
864 rend = ymin/prom_t_eos_table(1)
865 t = max(tiny(1.d0), y/rend)
870 rend = ymax/prom_t_eos_table(n_prom_eos)
878 do while (ihi-ilo > 1)
880 ymid = (1.d0-wp)*prom_y_eos_table(imid,ip) + &
881 wp*prom_y_eos_table(imid,ip+1)
890 ylo = (1.d0-wp)*prom_y_eos_table(ilo,ip) + &
891 wp*prom_y_eos_table(ilo,ip+1)
893 yhi = (1.d0-wp)*prom_y_eos_table(ilo+1,ip) + &
894 wp*prom_y_eos_table(ilo+1,ip+1)
897 if (denom <= max(tiny(denom),1.d-12*abs(ylo)))
then
898 call mpistop(
"invalid prominence EOS inversion interval")
901 t = prom_t_eos_table(ilo) + (y-ylo) * &
902 (prom_t_eos_table(ilo+1)-prom_t_eos_table(ilo)) / &
904 end subroutine ionization_invert_y_prominence_scalar
906 subroutine ionization_build_eos_table()
909 double precision :: iz_h, iz_he
911 if (.not.
allocated(te_h_table) .or. .not.
allocated(iz_h_table))
then
912 call mpistop(
"ionization_build_eos_table: ionization tables are not initialized")
915 if (
allocated(rfactor_table))
deallocate(rfactor_table)
916 if (
allocated(y_table))
deallocate(y_table)
917 if (
allocated(eps_ion_h_table))
deallocate(eps_ion_h_table)
922 call mpistop(
"ionization_build_eos_table: Te_H_table has fewer than 2 points")
925 allocate(rfactor_table(1:n))
926 allocate(y_table(1:n))
927 if (ionization_include_energy)
allocate(eps_ion_h_table(1:n))
931 te_h_table(i), rfactor_table(i), iz_h, iz_he)
932 y_table(i) = rfactor_table(i)*te_h_table(i)
933 if (ionization_include_energy)
then
934 eps_ion_h_table(i) = ionization_eps_ion_of_degrees(iz_h, iz_he)
938 y_table_min = y_table(1)
939 y_table_max = y_table(n)
941 call ionization_check_y_table()
942 end subroutine ionization_build_eos_table
944 subroutine ionization_check_y_table()
948 if (.not.
allocated(y_table))
then
949 call mpistop(
"ionization_check_y_table: Y_table is not allocated")
952 if (
size(y_table) < 2)
then
953 call mpistop(
"ionization_check_y_table: Y_table has fewer than 2 points")
956 do i = 1,
size(y_table)-1
957 if (y_table(i+1) <= y_table(i))
then
958 call mpistop(
"ionization_check_y_table: Y(T)=Rfactor(T)*T is not strictly increasing")
961 end subroutine ionization_check_y_table
963 double precision function ionization_rfactor_from_degrees(iz_H, iz_He)
964 double precision,
intent(in) :: iz_h, iz_he
966 ionization_rfactor_from_degrees = &
967 (1.d0 + iz_h + ionization_he_abundance * &
968 (1.d0 + iz_he*(1.d0 + iz_he))) / ionization_rfactor_norm
969 end function ionization_rfactor_from_degrees
977 double precision function ionization_eps_ion_of_degrees(iz_H, iz_He)
result(eps_ion)
978 double precision,
intent(in) :: iz_h, iz_he
980 eps_ion = ionization_h_energy_unit*iz_h &
981 + ionization_he_abundance * ( ionization_hei_energy_unit*iz_he &
982 + ionization_heii_energy_unit*iz_he*iz_he )
983 end function ionization_eps_ion_of_degrees
987 ionization_table_mode == ionization_mode_chromosphere .or. &
988 ionization_table_mode == ionization_mode_flare
991 logical function ionization_includes_energy()
992 ionization_includes_energy = ionization_include_energy
993 end function ionization_includes_energy
995 double precision function ionization_eint_table_value(i, invgam)
996 integer,
intent(in) :: i
997 double precision,
intent(in) :: invgam
999 ionization_eint_table_value = &
1000 y_table(i)*invgam + eps_ion_h_table(i)
1001 end function ionization_eint_table_value
1003 subroutine ionization_check_eint_table(invgam)
1005 double precision,
intent(in) :: invgam
1007 double precision :: eps1, eps2
1009 if (.not. ionization_include_energy)
return
1010 if (.not.
allocated(eps_ion_h_table))
then
1011 call mpistop(
"ionization-energy table is not initialized")
1013 if (invgam <= 0.d0)
then
1014 call mpistop(
"invalid inverse gamma minus one for ionization EOS")
1017 do i = 1,
size(y_table)-1
1018 eps1 = ionization_eint_table_value(i, invgam)
1019 eps2 = ionization_eint_table_value(i+1, invgam)
1020 if (eps2 <= eps1)
then
1021 call mpistop(
"ionization specific-energy table is not monotonic")
1024 end subroutine ionization_check_eint_table
1026 subroutine ionization_invert_eint_scalar(eps, invgam, T)
1030 double precision,
intent(in) :: eps, invgam
1031 double precision,
intent(out) :: t
1033 integer :: ilo, ihi, imid, n, iter
1034 double precision :: eps_min, eps_max, eps_lo, eps_hi, denom
1035 double precision :: eps_eval, slope, rfactor, iz_h, iz_he, tnew
1037 if (.not. ionization_include_energy .or. &
1038 .not.
allocated(eps_ion_h_table))
then
1039 call mpistop(
"ionization-energy EOS is not initialized")
1041 if (invgam <= 0.d0)
then
1042 call mpistop(
"invalid inverse gamma minus one in energy inversion")
1046 eps_min = ionization_eint_table_value(1, invgam)
1047 eps_max = ionization_eint_table_value(n, invgam)
1049 if (eps < eps_min)
then
1052 t = max(tiny(1.d0), &
1053 (eps-eps_ion_h_table(1))/(rfactor_table(1)*invgam))
1057 if (eps >= eps_max)
then
1058 t = max(tiny(1.d0), &
1059 (eps-eps_ion_h_table(n))/(rfactor_table(n)*invgam))
1065 do while (ihi-ilo > 1)
1067 if (eps >= ionization_eint_table_value(imid, invgam))
then
1074 eps_lo = ionization_eint_table_value(ilo, invgam)
1075 eps_hi = ionization_eint_table_value(ilo+1, invgam)
1076 denom = eps_hi-eps_lo
1077 if (denom <= max(tiny(denom), 1.
d-12*abs(eps_lo)))
then
1078 call mpistop(
"invalid ionization-energy inversion interval")
1081 t = te_h_table(ilo) + (eps-eps_lo) * &
1082 (te_h_table(ilo+1)-te_h_table(ilo))/denom
1087 slope = denom/(te_h_table(ilo+1)-te_h_table(ilo))
1090 t, rfactor, iz_h, iz_he)
1091 eps_eval = rfactor*t*invgam + ionization_eps_ion_of_degrees(iz_h, iz_he)
1092 if (abs(eps_eval-eps) <= &
1093 1.
d-12*max(abs(eps), tiny(1.d0)))
exit
1094 tnew = t-(eps_eval-eps)/slope
1095 t = max(te_h_table(ilo), min(te_h_table(ilo+1), tnew))
1097 if (iter > 3 .and. .not. pi_newton_warned .and.
mype == 0)
then
1098 pi_newton_warned = .true.
1099 write(*,*)
"WARNING: PI eint->T Newton inversion did not fully converge ", &
1100 "in 3 iterations; using the last iterate (interval-clamped). ", &
1101 "Further occurrences are not reported."
1103 end subroutine ionization_invert_eint_scalar
1106 rho, eint, invgam, T, p, Rfactor, iz_H, iz_He)
1108 double precision,
intent(in) :: rho, eint, invgam
1109 double precision,
intent(out) :: t, p, rfactor
1110 double precision,
intent(out),
optional :: iz_h, iz_he
1111 double precision :: iz_h_loc, iz_he_loc
1113 if (.not. ionization_include_energy) &
1114 call mpistop(
"ionization-energy EOS is not enabled")
1116 call mpistop(
"ionization-energy EOS requires a T-only table")
1117 if (rho <= 0.d0 .or. eint <= 0.d0) &
1118 call mpistop(
"invalid rho or eint in ionization EOS")
1119 call ionization_invert_eint_scalar(eint/rho, invgam, t)
1121 t, rfactor, iz_h_loc, iz_he_loc)
1123 if (
present(iz_h)) iz_h = iz_h_loc
1124 if (
present(iz_he)) iz_he = iz_he_loc
1128 rho, T, invgam, p, eint, Rfactor, iz_H, iz_He)
1130 double precision,
intent(in) :: rho, t, invgam
1131 double precision,
intent(out) :: p, eint, rfactor
1132 double precision,
intent(out),
optional :: iz_h, iz_he
1133 double precision :: iz_h_loc, iz_he_loc
1135 if (rho <= 0.d0 .or. t <= 0.d0 .or. invgam <= 0.d0)
then
1136 call mpistop(
"invalid state in rho,T ionization EOS mapping")
1139 if (ionization_include_energy)
then
1141 call mpistop(
"ionization energy requires a T-only ionization table")
1144 t, rfactor, iz_h_loc, iz_he_loc)
1146 eint = rho*(rfactor*t*invgam + &
1147 ionization_eps_ion_of_degrees(iz_h_loc, iz_he_loc))
1154 if (
present(iz_h)) iz_h = iz_h_loc
1155 if (
present(iz_he)) iz_he = iz_he_loc
1158 subroutine ionization_get_eps_derivative_t( &
1159 T, invgam, eps, deps_dT, dq_dT_out)
1161 double precision,
intent(in) :: t, invgam
1162 double precision,
intent(out) :: eps, deps_dt
1163 double precision,
intent(out),
optional :: dq_dt_out
1166 double precision :: rfactor, iz_h, iz_he, q, dq_dt, depsion_dt
1168 if (.not. ionization_include_energy)
then
1169 call mpistop(
"ionization-energy derivative requested while disabled")
1172 call mpistop(
"ionization-energy derivative requires a T-only table")
1174 if (t <= 0.d0 .or. invgam <= 0.d0)
then
1175 call mpistop(
"invalid state in ionization-energy derivative")
1178 n =
size(te_h_table)
1181 eps = q*invgam + ionization_eps_ion_of_degrees(iz_h, iz_he)
1183 if (t < te_table_min)
then
1184 dq_dt = rfactor_table(1)
1186 else if (t >= te_table_max)
then
1187 dq_dt = rfactor_table(n)
1190 i = int((t-te_table_min)*inv_te_table_step) + 1
1191 i = max(1, min(n-1, i))
1192 dq_dt = (y_table(i+1)-y_table(i)) / &
1193 (te_h_table(i+1)-te_h_table(i))
1194 depsion_dt = (eps_ion_h_table(i+1)-eps_ion_h_table(i)) / &
1195 (te_h_table(i+1)-te_h_table(i))
1198 deps_dt = dq_dt*invgam + depsion_dt
1199 if (deps_dt <= 0.d0)
then
1200 call mpistop(
"non-positive heat capacity in ionization EOS")
1202 if (
present(dq_dt_out)) dq_dt_out = dq_dt
1203 end subroutine ionization_get_eps_derivative_t
1206 double precision,
intent(in) :: t, invgam
1207 double precision,
intent(out) :: csound2
1209 double precision :: rfactor, q, eps, deps_dt, dq_dt
1211 call ionization_get_eps_derivative_t( &
1212 t, invgam, eps, deps_dt, dq_dt)
1215 csound2 = q*(1.d0+dq_dt/deps_dt)
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Module for physical and numeric constants.
double precision, parameter const_ev
PI (partial-ionisation) ionisation-degree backend for the eos% family.
logical function, public ionization_is_temperature_only()
subroutine, public ionization_get_state(ixil, ixol, rho, p, t, rfactor, iz_h, iz_he)
subroutine, public ionization_get_state_scalar(rho, p, t, rfactor, iz_h, iz_he)
subroutine, public ionization_get_p_eint_from_rho_t(rho, t, invgam, p, eint, rfactor, iz_h, iz_he)
subroutine, public ionization_degree_init(he_abundance, rfactor_norm, table_name, include_energy)
subroutine, public ionization_state_from_temperature_scalar(t, rfactor, iz_h, iz_he)
subroutine, public ionization_solve_p_rfactor(rho, t, p, rfactor)
subroutine, public ionization_degree_from_temperature(ixil, ixol, te, iz_h, iz_he)
Array interface for T -> ionization degrees. Used by get_Rfactor_tonly_PI (mod_eos_pi) to build the n...
subroutine, public ionization_get_state_from_eint(rho, eint, invgam, t, p, rfactor, iz_h, iz_he)
subroutine, public ionization_state_tp(t, p, rfactor, iz_h, iz_he)
subroutine, public ionization_get_csound2_t(t, invgam, csound2)
This module contains definitions of global parameters and variables and some generic functions/subrou...
integer it
Number of time steps taken.
double precision unit_numberdensity
Physical scaling factor for number density.
double precision unit_pressure
Physical scaling factor for pressure.
integer mype
The rank of the current MPI task.
double precision dt
global time step
double precision, dimension(:), allocatable, parameter d
double precision unit_temperature
Physical scaling factor for temperature.
logical si_unit
Use SI units (.true.) or use cgs units (.false.).