MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_radiative_cooling.t
Go to the documentation of this file.
1!> module radiative cooling -- add optically thin radiative cooling
2!>
3!> only uses the (Townsend) exact integration method, can be used in HD, ffhd, MHD, twofl
4!>
5!> Cooling curves assume an H/He plasma and ionization equilibrium.
6!> The gas temperature is supplied by the active physics EOS; the cooling
7!> table composition and abundance assumptions remain curve-dependent.
8!> Formula: Q=-n_H*n_e*f(T), where f(T) is tabulated or piecewise power law.
9!>
11
12 use mod_global_parameters, only: std_len
13 use mod_physics
15 use mod_comm_lib, only: mpistop
16 implicit none
17
18 !> Helium abundance over Hydrogen
19 double precision, private :: He_abundance
20
21 !> The adiabatic index
22 double precision, private :: rc_gamma
23
24 !> The adiabatic index minus 1
25 double precision, private :: rc_gamma_1
26
27 !> inverse of the adiabatic index minus 1
28 double precision, private :: invgam
29
30 abstract interface
31 subroutine get_subr1(w,x,ixI^L,ixO^L,res)
33 integer, intent(in) :: ixI^L, ixO^L
34 double precision, intent(in) :: w(ixI^S,nw)
35 double precision, intent(in) :: x(ixI^S,1:ndim)
36 double precision, intent(out):: res(ixI^S)
37 end subroutine get_subr1
38
39 subroutine get_rfactor_t(T, Rfactor)
40 double precision, intent(in) :: T
41 double precision, intent(out) :: Rfactor
42 end subroutine get_rfactor_t
43
44 subroutine get_pthermal_rfactor_rho_t(rho, T, pthermal, Rfactor)
45 double precision, intent(in) :: rho, T
46 double precision, intent(out) :: pthermal, Rfactor
47 end subroutine get_pthermal_rfactor_rho_t
48
50 rho, T, pthermal, eint, Rfactor)
51 double precision, intent(in) :: rho, T
52 double precision, intent(out) :: pthermal, eint, Rfactor
54
56 T, invgam, eps, deps_dT, dq_dT)
57 double precision, intent(in) :: T, invgam
58 double precision, intent(out) :: eps, deps_dT
59 double precision, intent(out), optional :: dq_dT
60 end subroutine get_eps_derivative_t
61 end interface
62
64
65 double precision :: rad_damp_height
66 double precision :: rad_damp_scale
67
68 ! these are set in init method
69 double precision, allocatable :: tcool(:), lcool(:), dldtcool(:)
70 double precision, allocatable :: yc(:)
71 double precision, allocatable :: teion(:), yeion(:)
72 double precision :: tref, lref, tcoolmin,tcoolmax
73 double precision :: lgtcoolmin, lgtcoolmax, lgstep
74 double precision :: eion_lgtmin, eion_lgstep
75
76 ! The piecewise powerlaw (PPL) tabels and variabels
77 ! x_* en t_* are given as log_10
78 double precision, allocatable :: y_ppl(:), t_ppl(:), l_ppl(:), a_ppl(:)
79
80 !> Lower limit of temperature
81 double precision :: tlow
82
83 !> Index of the energy density
84 integer :: e_
85 !> Index of cut off temperature for TRAC
86 integer :: tcoff_
87
88 ! these are set as parameters
89 !> Resolution of temperature in interpolated tables
90 integer :: ncool
91
92 integer :: n_ppl
93
94 !> Fixed temperature not lower than tlow
95 logical :: tfix
96
97 !> Add cooling source in a split way (.true.) or un-split way (.false.)
98 logical :: rc_split
99
100 logical :: isppl = .false.
101 logical :: has_eion_table = .false.
102
103 !> Apply radiative damping near both x1 boundaries for 1D loop models
104 logical :: rc_is_1d_loop = .false.
105 !> cutoff radiative cooling below rad_damp_height
106 logical :: rad_damp
107 !> whether background equilibrium contribution is split off
108 logical :: has_equi = .false.
109 !> whether background equilibrium is compensated in thermal balance
110 logical :: subtract_equi = .false.
111
112 double precision, allocatable :: frac_lowfip(:)
113 !> Index of primitive FIP abundance variable, -1 if disabled
114 integer :: fip_ = -1
115 !> Enable local Newton cooling/heating approximation for optically thick losses
116 logical :: rad_newton = .false.
117 double precision :: rad_newton_pthick = 25.d0
118 double precision :: rad_newton_trad = 0.006d0
119 double precision :: rad_newton_rhosurf = 1.d4
120
121 !> Name of cooling curve
122 character(len=std_len) :: coolcurve
123
124 procedure(get_subr1), pointer, nopass :: get_rho => null()
125 procedure(get_subr1), pointer, nopass :: get_rho_equi => null()
126 procedure(get_subr1), pointer, nopass :: get_pthermal => null()
127 procedure(get_subr1), pointer, nopass :: get_temperature => null()
128 procedure(get_subr1), pointer, nopass :: get_pthermal_equi => null()
129 procedure(get_subr1), pointer, nopass :: get_var_rfactor => null()
130 procedure(get_rfactor_t), pointer, nopass :: get_rfactor_from_temperature => null()
131 procedure(get_subr1), pointer, nopass :: get_temperature_equi => null()
132 procedure(get_pthermal_rfactor_rho_t), pointer, nopass :: get_pthermal_rfactor_from_rho_t => null()
133 procedure(get_subr1), pointer, nopass :: get_eint => null()
134 procedure(get_pthermal_eint_rfactor_rho_t), pointer, nopass :: &
135 get_pthermal_eint_rfactor_from_rho_t => null()
136 procedure(get_eps_derivative_t), pointer, nopass :: &
137 get_eps_derivative_from_t => null()
138
139 end type rc_fluid
140
141 contains
142
143 !> Radiative cooling initialization
144 subroutine radiative_cooling_init_params(phys_gamma,He_abund)
146 double precision, intent(in) :: phys_gamma,He_abund
147
148 rc_gamma=phys_gamma
149 he_abundance=he_abund
150 end subroutine radiative_cooling_init_params
151
152 subroutine radiative_cooling_init(fl,read_params)
154 interface
155 subroutine read_params(fl)
157 import rc_fluid
158 type(rc_fluid), intent(inout) :: fl
159
160 end subroutine read_params
161 end interface
162
163 type(rc_fluid), intent(inout) :: fl
164
165 double precision, dimension(:), allocatable :: t_table
166 double precision, dimension(:), allocatable :: L_table
167 double precision, dimension(:), allocatable :: f_table
168 double precision :: ratt, fact1, fact2, fact3, dL1, dL2
169 double precision :: tstep, Lstep
170 integer :: ntable, i, j
171 logical :: jump
172 Character(len=65) :: PPL_curves(1:6)
173
174 fl%ncool=4000
175 fl%coolcurve='JCcorona'
176 fl%tlow=bigdouble
177 fl%Tfix=.false.
178 fl%rc_split=.false.
179 fl%rc_is_1d_loop = .false.
180 fl%rad_damp=.false.
181 fl%rad_damp_height=0.5d0
182 fl%rad_damp_scale=0.15d0
183 call read_params(fl)
184
185 if (fl%fip_ > 0) then
186 select case (trim(fl%coolcurve))
187 case ('Dere_photo', 'Dere_photo_DM')
188 case default
189 call mpistop("FIP cooling requires coolcurve='Dere_photo' or 'Dere_photo_DM'")
190 end select
191 end if
192
193 if(fl%rc_split) any_source_split=.true.
194
195 ! Checks if coolcurve is a piecewise power law (PPL)
196 ppl_curves = [Character(len=65) :: 'Hildner','FM', 'Rosner', 'Klimchuk','SPEX_DM_rough','SPEX_DM_fine']
197 do i=1,size(ppl_curves)
198 if (ppl_curves(i)==fl%coolcurve) then
199 fl%isPPL = .true.
200 end if
201 end do
202
203 ! Init for PPL
204 if (fl%isPPL) then
205 ! Read in tables and create t_PPL, l_PPL, a_PPL
206 select case(fl%coolcurve)
207
208 case('Hildner')
209 if(mype ==0) &
210 print *,'Use Hildner (1974) piecewise power law'
211 fl%n_PPL = n_hildner
212 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
213 allocate(fl%a_PPL(1:fl%n_PPL))
214 fl%t_PPL(1:fl%n_PPL+1) = t_hildner(1:n_hildner+1)
215 fl%a_PPL(1:fl%n_PPL) = a_hildner(1:n_hildner)
216 fl%l_PPL(1:fl%n_PPL) = 10.d0**x_hildner(1:n_hildner) * (10.d0**fl%t_PPL(1:fl%n_PPL))**fl%a_PPL(1:fl%n_PPL)
217
218 case('FM')
219 if(mype==0) &
220 print *,'Use Forbes and Malherbe (1991)-like piecewise power law'
221 fl%n_PPL = n_fm
222 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
223 allocate(fl%a_PPL(1:fl%n_PPL))
224 fl%t_PPL(1:fl%n_PPL+1) = t_fm(1:n_fm+1)
225 fl%a_PPL(1:fl%n_PPL) = a_fm(1:n_fm)
226 fl%l_PPL(1:fl%n_PPL) = 10.d0**x_fm(1:n_fm) * (10.d0**fl%t_PPL(1:fl%n_PPL))**fl%a_PPL(1:fl%n_PPL)
227
228 case('Rosner')
229 if(mype==0) &
230 print *,'Use piecewise power law according to Rosner (1978)'
231 if(mype ==0) &
232 print *,'and extended by Priest (1982) from Van Der Linden (1991)'
233 fl%n_PPL = n_rosner
234 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
235 allocate(fl%a_PPL(1:fl%n_PPL))
236 fl%t_PPL(1:fl%n_PPL+1) = t_rosner(1:n_rosner+1)
237 fl%a_PPL(1:fl%n_PPL) = a_rosner(1:n_rosner)
238 fl%l_PPL(1:fl%n_PPL) = 10.d0**x_rosner(1:n_rosner) * (10.d0**fl%t_PPL(1:fl%n_PPL))**fl%a_PPL(1:fl%n_PPL)
239
240 case('Klimchuk')
241 if(mype==0) &
242 print *,'Use Klimchuk (2008) piecewise power law'
243 fl%n_PPL = n_klimchuk
244 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
245 allocate(fl%a_PPL(1:fl%n_PPL))
246 fl%t_PPL(1:fl%n_PPL+1) = t_klimchuk(1:n_klimchuk+1)
247 fl%a_PPL(1:fl%n_PPL) = a_klimchuk(1:n_klimchuk)
248 fl%l_PPL(1:fl%n_PPL) = 10.d0**x_klimchuk(1:n_klimchuk) * (10.d0**fl%t_PPL(1:fl%n_PPL))**fl%a_PPL(1:fl%n_PPL)
249
250 case('SPEX_DM_rough')
251 if(mype==0) &
252 print *,'Use the rough piece wise power law fit to the SPEX_DM curve (2009)'
253 fl%n_PPL = n_spex_dm_rough
254 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
255 allocate(fl%a_PPL(1:fl%n_PPL))
256 fl%t_PPL(1:fl%n_PPL+1) = t_spex_dm_rough(1:n_spex_dm_rough+1)
257 fl%a_PPL(1:fl%n_PPL) = a_spex_dm_rough(1:n_spex_dm_rough)
258 fl%l_PPL(1:fl%n_PPL) = 10.d0**x_spex_dm_rough(1:n_spex_dm_rough) * (10.d0**fl%t_PPL(1:fl%n_PPL))**fl%a_PPL(1:fl%n_PPL)
259
260 case('SPEX_DM_fine')
261 if(mype==0) &
262 print *,'Use the fine, detailed piece wise power law fit to the SPEX_DM curve (2009)'
263 fl%n_PPL = n_spex_dm_fine
264 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
265 allocate(fl%a_PPL(1:fl%n_PPL))
266 fl%t_PPL(1:fl%n_PPL+1) = t_spex_dm_fine(1:n_spex_dm_fine+1)
267 fl%a_PPL(1:fl%n_PPL) = a_spex_dm_fine(1:n_spex_dm_fine)
268 fl%l_PPL(1:fl%n_PPL) = 10.d0**x_spex_dm_fine(1:n_spex_dm_fine) * (10.d0**fl%t_PPL(1:fl%n_PPL))**fl%a_PPL(1:fl%n_PPL)
269
270 case default
271 call mpistop("This piecewise power law is unknown")
272 end select
273
274 ! Go from logarithmic to actual values.
275 fl%t_PPL(1:fl%n_PPL+1) = 10.d0**fl%t_PPL(1:fl%n_PPL+1)
276 ! Change unit of table if SI is used instead of cgs
277 if (si_unit) fl%l_PPL(1:fl%n_PPL) = fl%l_PPL(1:fl%n_PPL) * 10.0d0**(-13)
278
279 ! Make dimensionless
280 fl%t_PPL(1:fl%n_PPL+1) = fl%t_PPL(1:fl%n_PPL+1) / unit_temperature
281 fl%l_PPL(1:fl%n_PPL) = fl%l_PPL(1:fl%n_PPL) * unit_numberdensity**2 * unit_time / unit_pressure * (1.d0+2.d0*he_abundance)
282
283 ! Set tref en lref
284 fl%l_PPL(fl%n_PPL+1) = fl%l_PPL(fl%n_PPL) * ( fl%t_PPL(fl%n_PPL+1) / fl%t_PPL(fl%n_PPL) )**fl%a_PPL(fl%n_PPL)
285 fl%lref = fl%l_PPL(fl%n_PPL+1)
286 fl%tref = fl%t_PPL(fl%n_PPL+1)
287
288 ! Set tcoolmin and tcoolmax
289 fl%tcoolmin = fl%t_PPL(1)
290 fl%tcoolmax = fl%t_PPL(fl%n_PPL+1)
291 ! smaller value for lowest temperatures from cooling table and user's choice
292 if (fl%tlow==bigdouble) fl%tlow=fl%tcoolmin
293 !create y_PPL
294 call create_y_ppl(fl)
295
296 else
297
298 ! Init for interpolatable tables
299 allocate(fl%tcool(1:fl%ncool), fl%Lcool(1:fl%ncool), fl%dLdtcool(1:fl%ncool))
300 allocate(fl%Yc(1:fl%ncool))
301 if(fl%fip_ > 0) allocate(fl%frac_lowFIP(1:fl%ncool))
302
303 fl%tcool(1:fl%ncool) = zero
304 fl%Lcool(1:fl%ncool) = zero
305 fl%dLdtcool(1:fl%ncool) = zero
306
307 ! Read in the selected cooling curve
308 select case(fl%coolcurve)
309
310 case('JCcorona')
311 if(mype ==0) &
312 print *,'Use Colgan & Feldman (2008) cooling curve'
313 if(mype ==0) &
314 print *,'This version only till 10000 K, beware for floor T treatment'
315 ntable = n_jccorona
316 allocate(t_table(1:ntable))
317 allocate(l_table(1:ntable))
318 t_table(1:ntable) = t_jccorona(1:n_jccorona)
319 l_table(1:ntable) = l_jccorona(1:n_jccorona)
320
321 case('DM')
322 if(mype ==0) &
323 print *,'Use Dalgarno & McCray (1972) cooling curve'
324 ntable = n_dm
325 allocate(t_table(1:ntable))
326 allocate(l_table(1:ntable))
327 t_table(1:ntable) = t_dm(1:n_dm)
328 l_table(1:ntable) = l_dm(1:n_dm)
329
330 case('MB')
331 if(mype ==0) &
332 write(*,'(3a)') 'Use MacDonald & Bailey (1981) cooling curve '&
333 ,'as implemented in ZEUS-3D, with the values '&
334 ,'from Dalgarno & McCRay (1972) for low temperatures.'
335 ntable = n_mb + 20
336 allocate(t_table(1:ntable))
337 allocate(l_table(1:ntable))
338 t_table(1:ntable) = t_dm(1:21)
339 l_table(1:ntable) = l_dm(1:21)
340 t_table(22:ntable) = t_mb(2:n_mb)
341 l_table(22:ntable) = l_mb(2:n_mb)
342
343 case('MLcosmol')
344 if(mype ==0) &
345 print *,'Use Mellema & Lundqvist (2002) cooling curve '&
346 ,'for zero metallicity '
347 ntable = n_mlcosmol
348 allocate(t_table(1:ntable))
349 allocate(l_table(1:ntable))
350 t_table(1:ntable) = t_mlcosmol(1:n_mlcosmol)
351 l_table(1:ntable) = l_mlcosmol(1:n_mlcosmol)
352
353 case('MLwc')
354 if(mype ==0) &
355 print *,'Use Mellema & Lundqvist (2002) cooling curve '&
356 ,'for WC-star metallicity '
357 ntable = n_mlwc
358 allocate(t_table(1:ntable))
359 allocate(l_table(1:ntable))
360 t_table(1:ntable) = t_mlwc(1:n_mlwc)
361 l_table(1:ntable) = l_mlwc(1:n_mlwc)
362
363 case('MLsolar1')
364 if(mype ==0) &
365 print *,'Use Mellema & Lundqvist (2002) cooling curve '&
366 ,'for solar metallicity '
367 ntable = n_mlsolar1
368 allocate(t_table(1:ntable))
369 allocate(l_table(1:ntable))
370 t_table(1:ntable) = t_mlsolar1(1:n_mlsolar1)
371 l_table(1:ntable) = l_mlsolar1(1:n_mlsolar1)
372
373 case('cloudy_ism')
374 if(mype ==0) &
375 print *,'Use Cloudy based cooling curve '&
376 ,'for ism metallicity '
377 ntable = n_cl_ism
378 allocate(t_table(1:ntable))
379 allocate(l_table(1:ntable))
380 t_table(1:ntable) = t_cl_ism(1:n_cl_ism)
381 l_table(1:ntable) = l_cl_ism(1:n_cl_ism)
382
383 case('cloudy_solar')
384 if(mype ==0) &
385 print *,'Use Cloudy based cooling curve '&
386 ,'for solar metallicity '
387 ntable = n_cl_solar
388 allocate(t_table(1:ntable))
389 allocate(l_table(1:ntable))
390 t_table(1:ntable) = t_cl_solar(1:n_cl_solar)
391 l_table(1:ntable) = l_cl_solar(1:n_cl_solar)
392
393 case('SPEX')
394 if(mype ==0) &
395 print *,'Use SPEX cooling curve (Schure et al. 2009) '&
396 ,'for solar metallicity '
397 ntable = n_spex
398 allocate(t_table(1:ntable))
399 allocate(l_table(1:ntable))
400 t_table(1:ntable) = t_spex(1:n_spex)
401 l_table(1:ntable) = l_spex(1:n_spex) + log10(nenh_spex(1:n_spex))
402
403 case('SPEX_DM')
404 if(mype ==0) then
405 print *, 'Use SPEX cooling curve for solar metallicity above 10^4 K. '
406 print *, 'At lower temperatures,use Dalgarno & McCray (1972), '
407 print *, 'with a pre-set ionization fraction of 10^-3. '
408 print *, 'as described by Schure et al. (2009). '
409 endif
410 ntable = n_spex + n_dm_2 - 6
411 allocate(t_table(1:ntable))
412 allocate(l_table(1:ntable))
413 t_table(1:n_dm_2-1) = t_dm_2(1:n_dm_2-1)
414 l_table(1:n_dm_2-1) = l_dm_2(1:n_dm_2-1)
415 t_table(n_dm_2:ntable) = t_spex(6:n_spex)
416 l_table(n_dm_2:ntable) = l_spex(6:n_spex) + log10(nenh_spex(6:n_spex))
417
418 case('Dere_corona')
419 if(mype ==0) &
420 print *,'Use Dere (2009) cooling curve for solar corona'
421 ntable = n_dere
422 allocate(t_table(1:ntable))
423 allocate(l_table(1:ntable))
424 t_table(1:ntable) = t_dere(1:n_dere)
425 l_table(1:ntable) = l_dere_corona(1:n_dere)
426
427 case('Dere_corona_DM')
428 if(mype==0)&
429 print *, 'Combination of Dere_corona (2009) for high temperatures and'
430 if(mype==0)&
431 print *, 'Dalgarno & McCray (1972), DM2, for low temperatures'
432 ntable = n_dere + n_dm_2 - 1
433 allocate(t_table(1:ntable))
434 allocate(l_table(1:ntable))
435 t_table(1:n_dm_2-1) = t_dm_2(1:n_dm_2-1)
436 l_table(1:n_dm_2-1) = l_dm_2(1:n_dm_2-1)
437 t_table(n_dm_2:ntable) = t_dere(1:n_dere)
438 l_table(n_dm_2:ntable) = l_dere_corona(1:n_dere)
439
440 case('Dere_photo')
441 if(mype ==0) &
442 print *,'Use Dere (2009) cooling curve for solar photophere'
443 ntable = n_dere
444 allocate(t_table(1:ntable))
445 allocate(l_table(1:ntable))
446 if (fl%fip_ > 0) allocate(f_table(1:ntable))
447 t_table(1:ntable) = t_dere(1:n_dere)
448 l_table(1:ntable) = l_dere_photo(1:n_dere)
449 if (fl%fip_ > 0) f_table(1:ntable) = lowfip_frac(1:n_dere)
450
451 case('Dere_photo_DM')
452 if(mype==0)&
453 print *, 'Combination of Dere_photo (2009) for high temperatures and'
454 if(mype==0)&
455 print *, 'Dalgarno & McCray (1972), DM2, for low temperatures'
456 ntable = n_dere + n_dm_2 - 1
457 allocate(t_table(1:ntable))
458 allocate(l_table(1:ntable))
459 if (fl%fip_ > 0) allocate(f_table(1:ntable))
460 t_table(1:n_dm_2-1) = t_dm_2(1:n_dm_2-1)
461 l_table(1:n_dm_2-1) = l_dm_2(1:n_dm_2-1)
462 t_table(n_dm_2:ntable) = t_dere(1:n_dere)
463 l_table(n_dm_2:ntable) = l_dere_photo(1:n_dere)
464 if (fl%fip_ > 0) then
465 f_table(1:n_dm_2-1) = zero
466 f_table(n_dm_2:ntable) = lowfip_frac(1:n_dere)
467 end if
468
469 case('Colgan')
470 if(mype==0) &
471 print *, 'Use Colgan (2008) cooling curve'
472 ntable = n_colgan
473 allocate(t_table(1:ntable))
474 allocate(l_table(1:ntable))
475 t_table(1:ntable) = t_colgan(1:n_colgan)
476 l_table(1:ntable) = l_colgan(1:n_colgan)
477
478 case('Colgan_DM')
479 if(mype==0)&
480 print *, 'Combination of Colgan (2008) for high temperatures and'
481 if(mype==0)&
482 print *, 'Dalgarno & McCray (1972), DM2, for low temperatures'
483 ntable = n_colgan + n_dm_2
484 allocate(t_table(1:ntable))
485 allocate(l_table(1:ntable))
486 t_table(1:n_dm_2) = t_dm_2(1:n_dm_2)
487 l_table(1:n_dm_2) = l_dm_2(1:n_dm_2)
488 t_table(n_dm_2+1:ntable) = t_colgan(1:n_colgan)
489 l_table(n_dm_2+1:ntable) = l_colgan(1:n_colgan)
490
491 case default
492 call mpistop("This coolingcurve is unknown")
493 end select
494
495 ! create cooling table(s) for use in amrvac
496 fl%tcoolmax = t_table(ntable)
497 fl%tcoolmin = t_table(1)
498 ratt = (fl%tcoolmax-fl%tcoolmin)/( dble(fl%ncool-1) + smalldouble)
499
500 fl%tcool(1) = fl%tcoolmin
501 fl%Lcool(1) = l_table(1)
502
503 fl%tcool(fl%ncool) = fl%tcoolmax
504 fl%Lcool(fl%ncool) = l_table(ntable)
505
506 if (fl%fip_ > 0) then
507 fl%frac_lowFIP(1) = f_table(1)
508 fl%frac_lowFIP(fl%ncool) = f_table(ntable)
509 end if
510
511 do i=2,fl%ncool ! loop to create one table
512 fl%tcool(i) = fl%tcool(i-1)+ratt
513 do j=1,ntable-1 ! loop to create one spot on a table
514 ! Second order polynomial interpolation, except at the outer edge,
515 ! or in case of a large jump.
516 if(fl%tcool(i) < t_table(j+1)) then
517 if(j.eq. ntable-1 )then
518 fact1 = (fl%tcool(i)-t_table(j+1)) &
519 /(t_table(j)-t_table(j+1))
520 fact2 = (fl%tcool(i)-t_table(j)) &
521 /(t_table(j+1)-t_table(j))
522 fl%Lcool(i) = l_table(j)*fact1 + l_table(j+1)*fact2
523 if (fl%fip_ > 0) then
524 fl%frac_lowFIP(i) = f_table(j)*fact1 + f_table(j+1)*fact2
525 end if
526 exit
527 else
528 dl1 = l_table(j+1)-l_table(j)
529 dl2 = l_table(j+2)-l_table(j+1)
530 jump =(max(dabs(dl1),dabs(dl2)) > 2*min(dabs(dl1),dabs(dl2)))
531 end if
532 if( jump ) then
533 fact1 = (fl%tcool(i)-t_table(j+1)) &
534 /(t_table(j)-t_table(j+1))
535 fact2 = (fl%tcool(i)-t_table(j)) &
536 /(t_table(j+1)-t_table(j))
537 fl%Lcool(i) = l_table(j)*fact1 + l_table(j+1)*fact2
538 if (fl%fip_ > 0) then
539 fl%frac_lowFIP(i) = f_table(j)*fact1 + f_table(j+1)*fact2
540 end if
541 exit
542 else
543 fact1 = ((fl%tcool(i)-t_table(j+1)) &
544 * (fl%tcool(i)-t_table(j+2))) &
545 / ((t_table(j)-t_table(j+1)) &
546 * (t_table(j)-t_table(j+2)))
547 fact2 = ((fl%tcool(i)-t_table(j)) &
548 * (fl%tcool(i)-t_table(j+2))) &
549 / ((t_table(j+1)-t_table(j)) &
550 * (t_table(j+1)-t_table(j+2)))
551 fact3 = ((fl%tcool(i)-t_table(j)) &
552 * (fl%tcool(i)-t_table(j+1))) &
553 / ((t_table(j+2)-t_table(j)) &
554 * (t_table(j+2)-t_table(j+1)))
555 fl%Lcool(i) = l_table(j)*fact1 + l_table(j+1)*fact2 &
556 + l_table(j+2)*fact3
557 if (fl%fip_ > 0) then
558 fl%frac_lowFIP(i) = f_table(j)*fact1 + f_table(j+1)*fact2 &
559 + f_table(j+2)*fact3
560 end if
561 exit
562 end if
563 end if
564 end do ! end loop to find create one spot on a table
565 end do ! end loop to create one table
566
567 ! Go from logarithmic to actual values.
568 fl%tcool(1:fl%ncool) = 10.0d0**fl%tcool(1:fl%ncool)
569 fl%Lcool(1:fl%ncool) = 10.0d0**fl%Lcool(1:fl%ncool)
570
571 ! Change unit of table if SI is used instead of cgs
572 if (si_unit) fl%Lcool(1:fl%ncool) = fl%Lcool(1:fl%ncool) * 10.0d0**(-13)
573
574 ! Scale both T and Lambda
575 fl%tcool(1:fl%ncool) = fl%tcool(1:fl%ncool) / unit_temperature
576 fl%Lcool(1:fl%ncool) = fl%Lcool(1:fl%ncool) * unit_numberdensity**2 * unit_time / unit_pressure * (1.d0+2.d0*he_abundance)
577
578 fl%tcoolmin = fl%tcool(1)+smalldouble ! avoid pointless interpolation
579 ! smaller value for lowest temperatures from cooling table and user's choice
580 if (fl%tlow==bigdouble) fl%tlow=fl%tcoolmin
581 fl%tcoolmax = fl%tcool(fl%ncool)
582 fl%lgtcoolmin = dlog10(fl%tcoolmin)
583 fl%lgtcoolmax = dlog10(fl%tcoolmax)
584 fl%lgstep = (fl%lgtcoolmax-fl%lgtcoolmin) * 1.d0 / (fl%ncool-1)
585 fl%dLdtcool(1) = (fl%Lcool(2)-fl%Lcool(1))/(fl%tcool(2)-fl%tcool(1))
586 fl%dLdtcool(fl%ncool) = (fl%Lcool(fl%ncool)-fl%Lcool(fl%ncool-1))/(fl%tcool(fl%ncool)-fl%tcool(fl%ncool-1))
587
588 do i=2,fl%ncool-1
589 fl%dLdtcool(i) = (fl%Lcool(i+1)-fl%Lcool(i-1))/(fl%tcool(i+1)-fl%tcool(i-1))
590 end do
591
592 deallocate(t_table)
593 deallocate(l_table)
594 if (allocated(f_table)) deallocate(f_table)
595
596 fl%tref = fl%tcoolmax
597 fl%lref = fl%Lcool(fl%ncool)
598 fl%Yc(fl%ncool) = zero
599 do i=fl%ncool-1, 1, -1
600 fl%Yc(i) = fl%Yc(i+1)
601 do j=1,100
602 tstep = 1.0d-2*(fl%tcool(i+1)-fl%tcool(i))
603 call findl(fl%tcool(i+1)-j*tstep, lstep, fl)
604 fl%Yc(i) = fl%Yc(i) + fl%lref/fl%tref*tstep/lstep
605 end do
606 end do
607 end if
608
609 rc_gamma_1=rc_gamma-1.d0
610 invgam = 1.d0/rc_gamma_1
611 end subroutine radiative_cooling_init
612
614 use, intrinsic :: ieee_arithmetic, only: ieee_is_finite
616 type(rc_fluid), intent(inout) :: fl
617
618 integer :: i, j
619 double precision :: frac, Tleft, Tright, Tpoint, dtemp
620 double precision :: Lpoint, eps, deps_dT
621
622 if (.not. associated(fl%get_eps_derivative_from_T)) then
623 call mpistop("eion cooling table requires an EOS heat-capacity callback")
624 end if
625 if (rc_gamma_1 <= zero) then
626 call mpistop("invalid gamma for eion cooling table")
627 end if
628 if (fl%tcoolmin <= zero .or. fl%tcoolmax <= fl%tcoolmin) then
629 call mpistop("invalid temperature range for eion cooling table")
630 end if
631
632 if (allocated(fl%Teion)) deallocate(fl%Teion)
633 if (allocated(fl%Yeion)) deallocate(fl%Yeion)
634 allocate(fl%Teion(1:fl%ncool), fl%Yeion(1:fl%ncool))
635
636 fl%eion_lgtmin = dlog10(fl%tcoolmin)
637 fl%eion_lgstep = (dlog10(fl%tcoolmax)-fl%eion_lgtmin) / &
638 dble(fl%ncool-1)
639 do i = 1, fl%ncool
640 frac = dble(i-1)/dble(fl%ncool-1)
641 fl%Teion(i) = 10.d0**(fl%eion_lgtmin + &
642 frac*(dlog10(fl%tcoolmax)-fl%eion_lgtmin))
643 end do
644
645 ! Y_eion(T) = (Lref/Tref) integral_T^Tmax
646 ! [d eps(T')/dT']/Lambda(T') dT'.
647 ! With this normalization, dY/dt=(Lref/Tref)*rho.
648 fl%Yeion(fl%ncool) = zero
649 do i = fl%ncool-1, 1, -1
650 fl%Yeion(i) = fl%Yeion(i+1)
651 tleft = fl%Teion(i)
652 tright = fl%Teion(i+1)
653 dtemp = (tright-tleft)/100.d0
654 do j = 1, 100
655 tpoint = tleft+(dble(j)-half)*dtemp
656 call findl(tpoint, lpoint, fl)
657 call fl%get_eps_derivative_from_T( &
658 tpoint, invgam, eps, deps_dt)
659 if (.not. ieee_is_finite(lpoint) .or. lpoint <= zero) then
660 call mpistop("invalid Lambda in eion cooling table")
661 end if
662 if (.not. ieee_is_finite(deps_dt) .or. deps_dt <= zero) then
663 call mpistop("invalid heat capacity in eion cooling table")
664 end if
665 fl%Yeion(i) = fl%Yeion(i) + &
666 fl%lref/fl%tref*dtemp*deps_dt/lpoint
667 end do
668 if (fl%Yeion(i) <= fl%Yeion(i+1)) then
669 call mpistop("eion cooling transform is not monotonic")
670 end if
671 end do
672 fl%has_eion_table = .true.
674
675 subroutine create_y_ppl(fl)
676 ! creates the constants of integration needed for solving
677 ! the cooling law exact for a piecewise power law
678 ! In correspondence with eq. A6 of Townsend (2009)
680 type(rc_fluid) :: fl
681 double precision :: y_extra, factor
682 integer :: i
683
684 allocate(fl%y_PPL(1:fl%n_PPL+1))
685
686 fl%y_PPL(1:fl%n_PPL+1) = zero
687
688 do i=fl%n_PPL, 1, -1
689 factor = fl%l_PPL(fl%n_PPL+1) * fl%t_PPL(i) / (fl%l_PPL(i) * fl%t_PPL(fl%n_PPL+1))
690 if (fl%a_PPL(i) == 1.d0) then
691 y_extra = log( fl%t_PPL(i) / fl%t_PPL(i+1) )
692 else
693 y_extra = 1 / (1 - fl%a_PPL(i)) * (1 - ( fl%t_PPL(i) / fl%t_PPL(i+1) )**(fl%a_PPL(i)-1) )
694 end if
695 fl%y_PPL(i) = fl%y_PPL(i+1) - factor*y_extra
696 end do
697 end subroutine create_y_ppl
698
699 subroutine getvar_cooling(ixI^L,ixO^L,w,x,coolrate,fl)
700 ! Create extra variable to show cooling rate in the output.
701 ! This diagnostic returns the effective instantaneous optically-thin
702 ! radiative cooling rate.
703 ! Newton cooling is intentionally excluded here because it is not an
704 ! optically-thin radiative loss term.
706
707 integer, intent(in) :: ixI^L,ixO^L
708 double precision, intent(in) :: x(ixI^S,1:ndim)
709 double precision :: w(ixI^S,1:nw)
710 double precision, intent(out):: coolrate(ixI^S)
711 type(rc_fluid), intent(in) :: fl
712
713 double precision :: rho(ixI^S)
714 double precision :: L1, Te(ixI^S)
715 double precision :: rho_safe
716 double precision :: fip_prim, frac_lowFIP, fip_factor
717 double precision :: rad_damp_factor
718 integer :: ix^D
719
720 call fl%get_rho(w,x,ixi^l,ixo^l,rho)
721 call fl%get_temperature(w,x,ixi^l,ixo^l,te)
722
723 {do ix^db = ixo^lim^db\}
724 ! Ordinary optically-thin cooling curve contribution: rho^2 Lambda(T)
725 if(te(ix^d) <= fl%tcoolmin) then
726 l1 = zero
727 else if(te(ix^d) >= fl%tcoolmax) then
728 call calc_l_extended(te(ix^d),l1,fl)
729 l1 = l1*rho(ix^d)**2
730 else
731 call findl(te(ix^d),l1,fl)
732 l1 = l1*rho(ix^d)**2
733 end if
734
735 ! FIP-dependent correction.
736 ! In this routine w is conserved, so the tracer is rho*fip rather than fip.
737 fip_factor = one
738 if (fl%fip_ > 0) then
739 rho_safe = max(rho(ix^d), small_density)
740 fip_prim = min(maxfip, max(minfip, w(ix^d,fl%fip_) / rho_safe))
741 frac_lowfip = lowfip_fraction(te(ix^d), fl)
742 fip_factor = one - frac_lowfip + fip_prim * frac_lowfip
743 end if
744
745 ! Geometric damping of the optically-thin cooling, matching cool_exact.
746 rad_damp_factor = one
747 {^ifoned
748 if (slab_uniform .and. fl%rad_damp .and. x(ix^d,ndim) <= xprobmin1 + fl%rad_damp_height) then
749 rad_damp_factor = exp(-(x(ix^d,ndim)-xprobmin1-fl%rad_damp_height)**2/fl%rad_damp_scale**2)
750 end if
751 if (fl%rc_is_1d_loop .and. slab_uniform .and. fl%rad_damp &
752 .and. x(ix^d,ndim) >= xprobmax1 - fl%rad_damp_height) then
753 rad_damp_factor = exp(-(x(ix^d,ndim)-xprobmax1+fl%rad_damp_height)**2/fl%rad_damp_scale**2)
754 end if
755 }
756 {^iftwod
757 if (slab_uniform .and. fl%rad_damp .and. x(ix^d,ndim) <= xprobmin2 + fl%rad_damp_height) then
758 rad_damp_factor = exp(-(x(ix^d,ndim)-xprobmin2-fl%rad_damp_height)**2/fl%rad_damp_scale**2)
759 end if
760 }
761 {^ifthreed
762 if (slab_uniform .and. fl%rad_damp .and. x(ix^d,ndim) <= xprobmin3 + fl%rad_damp_height) then
763 rad_damp_factor = exp(-(x(ix^d,ndim)-xprobmin3-fl%rad_damp_height)**2/fl%rad_damp_scale**2)
764 end if
765 }
766
767 coolrate(ix^d) = l1 * fip_factor * rad_damp_factor
768 {end do\}
769 end subroutine getvar_cooling
770
771 subroutine getvar_cooling_exact(qdt, ixI^L, ixO^L, wCT, w, x, &
772 coolrate, fl)
773 ! Finite-step optically-thin cooling diagnostic based on the
774 ! Townsend exact-cooling temperature map.
775 !
776 ! This routine is not a complete mirror of cool_exact. It excludes
777 ! Newton cooling/heating, FIP corrections, geometric damping, TRAC
778 ! rescaling, and the source-ordering details of the actual update.
779 !
780 ! The dummy argument w is retained for calling-interface compatibility;
781 ! the diagnostic initial state is defined by wCT.
783
784 integer, intent(in) :: ixI^L, ixO^L
785 double precision, intent(in) :: qdt
786 double precision, intent(in) :: x(ixI^S,1:ndim)
787 double precision, intent(in) :: wCT(ixI^S,1:nw)
788 double precision, intent(in) :: w(ixI^S,1:nw)
789 double precision, intent(out) :: coolrate(ixI^S)
790 type(rc_fluid), intent(in) :: fl
791
792 double precision :: rho(ixI^S), Te(ixI^S), pthermal(ixI^S)
793 double precision :: eint_old(ixI^S)
794 double precision :: Y1, Y2, L1, Ttarget
795 double precision :: Rguess, Rtarget
796 double precision :: pfloor, ptarget, eint_floor, eint_target
797 double precision :: Lmax, fact
798 integer :: ix^D
799
800 if (qdt <= zero) then
801 call mpistop("getvar_cooling_exact requires qdt > 0")
802 end if
803 if (associated(fl%get_eps_derivative_from_T) .and. &
804 .not. fl%has_eion_table) then
805 call mpistop("eion exact-cooling table is not initialized")
806 end if
807 call fl%get_rho(wct, x, ixi^l, ixo^l, rho)
808 call fl%get_temperature(wct, x, ixi^l, ixo^l, te)
809 call fl%get_pthermal(wct, x, ixi^l, ixo^l, pthermal)
810 call cooling_get_eint( &
811 fl, wct, x, ixi^l, ixo^l, pthermal, eint_old)
812 fact = fl%lref*qdt/fl%tref
813 {do ix^db = ixo^lim^db\}
814 if (rho(ix^d) <= zero .or. te(ix^d) <= zero .or. &
815 pthermal(ix^d) <= zero) then
816 coolrate(ix^d) = zero
817 cycle
818 end if
819 if (te(ix^d) <= fl%tcoolmin) then
820 coolrate(ix^d) = zero
821 cycle
822 end if
823 rguess = pthermal(ix^d)/(rho(ix^d)*te(ix^d))
825 fl, rho(ix^d), fl%tlow, rguess, &
826 pfloor, eint_floor, rtarget)
827 lmax = max(zero, (eint_old(ix^d)-eint_floor)/qdt)
828 if (te(ix^d) >= fl%tcoolmax) then
829 call calc_l_extended(te(ix^d), l1, fl)
830 l1 = min(l1*rho(ix^d)**2, lmax)
831 else
832 ! Townsend Y(T) remains a one-dimensional approximation for a
833 ! pressure-dependent EOS. The endpoint energy is mapped consistently
834 ! through pthermal(rho,Ttarget).
835 if (fl%has_eion_table) then
836 call findy_eion(te(ix^d), y1, fl)
837 y2 = y1 + fact*rho(ix^d)
838 call findt_eion(ttarget, y2, fl)
839 else
840 call findy(te(ix^d), y1, fl)
841 y2 = y1 + fact*rho(ix^d)*rc_gamma_1
842 call findt(ttarget, y2, fl)
843 end if
844 if (ttarget <= fl%tcoolmin) then
845 l1 = lmax
846 else
848 fl, rho(ix^d), ttarget, rguess, &
849 ptarget, eint_target, rtarget)
850 l1 = max(zero, (eint_old(ix^d)-eint_target)/qdt)
851 l1 = min(l1, lmax)
852 end if
853 end if
854 coolrate(ix^d) = l1
855 {end do\}
856 end subroutine getvar_cooling_exact
857
858 subroutine radiative_cooling_add_source(qdt,ixI^L,ixO^L,wCT,wCTprim,w,x,&
859 qsourcesplit,active,fl)
860 ! w[iw]=w[iw]+qdt*S[wCT,x] where S is the source based on wCT within ixO
862 integer, intent(in) :: ixI^L, ixO^L
863 double precision, intent(in) :: qdt, x(ixI^S,1:ndim), wCT(ixI^S,1:nw), wCTprim(ixI^S,1:nw)
864 double precision, intent(inout) :: w(ixI^S,1:nw)
865 logical, intent(in) :: qsourcesplit
866 logical, intent(inout) :: active
867 type(rc_fluid), intent(in) :: fl
868 double precision, allocatable, dimension(:^D&) :: Lequi
869
870 if(qsourcesplit .eqv.fl%rc_split) then
871 active = .true.
872 call cool_exact(qdt,ixi^l,ixo^l,wct,wctprim,w,x,fl)
873 if(fl%subtract_equi) then
874 allocate(lequi(ixi^s))
875 call get_cool_equi(qdt,ixi^l,ixo^l,wct,w,x,fl,lequi)
876 w(ixo^s,fl%e_) = w(ixo^s,fl%e_)+lequi(ixo^s)
877 deallocate(lequi)
878 endif
879 if( fl%Tfix ) call floortemperature(qdt,ixi^l,ixo^l,wct,w,x,fl)
880 end if
881 end subroutine radiative_cooling_add_source
882
883 subroutine floortemperature(qdt,ixI^L,ixO^L,wCT,w,x,fl)
884 ! Force minimum temperature to a fixed temperature
886 integer, intent(in) :: ixI^L, ixO^L
887 double precision, intent(in) :: qdt, x(ixI^S,1:ndim)
888 double precision, intent(in) :: wCT(ixI^S,1:nw)
889 double precision, intent(inout) :: w(ixI^S,1:nw)
890 type(rc_fluid), intent(in) :: fl
891
892 double precision :: pthermal(ixI^S), rho(ixI^S)
893 double precision :: eint_current(ixI^S)
894 double precision :: temperature(ixI^S), Rfactor(ixI^S)
895 double precision :: Rguess, Rfloor, pfloor, eint_floor
896 integer :: ix^D
897
898 call fl%get_pthermal(w, x, ixi^l, ixo^l, pthermal)
899 call fl%get_rho(w, x, ixi^l, ixo^l, rho)
900 call cooling_get_eint( &
901 fl, w, x, ixi^l, ixo^l, pthermal, eint_current)
902
903 if (associated(fl%get_pthermal_Rfactor_from_rho_T)) then
904 ! Use the updated state and solve the floor EOS only where needed.
905 call fl%get_temperature(w, x, ixi^l, ixo^l, temperature)
906
907 {do ix^db = ixo^lim^db\}
908 if (temperature(ix^d) >= fl%tlow) cycle
909 if (rho(ix^d) > zero .and. temperature(ix^d) > zero) then
910 rguess = pthermal(ix^d) / (rho(ix^d)*temperature(ix^d))
911 else
912 rguess = one
913 end if
915 fl, rho(ix^d), fl%tlow, rguess, &
916 pfloor, eint_floor, rfloor)
917 if (eint_current(ix^d) < eint_floor) then
918 w(ix^d,fl%e_) = w(ix^d,fl%e_) + &
919 (eint_floor-eint_current(ix^d))
920 end if
921 {end do\}
922 else
923 ! Preserve the existing constant-R/user-Rfactor fallback behavior.
924 call fl%get_var_Rfactor(wct, x, ixi^l, ixo^l, rfactor)
925 {do ix^db = ixo^lim^db\}
927 fl, rho(ix^d), fl%tlow, rfactor(ix^d), &
928 pfloor, eint_floor, rfloor)
929 if (eint_current(ix^d) < eint_floor) then
930 w(ix^d,fl%e_) = w(ix^d,fl%e_) + &
931 (eint_floor-eint_current(ix^d))
932 end if
933 {end do\}
934 end if
935 end subroutine floortemperature
936
937 subroutine get_cool_equi(qdt,ixI^L,ixO^L,wCT,w,x,fl,res)
939
940 integer, intent(in) :: ixI^L, ixO^L
941 double precision, intent(in) :: qdt, x(ixI^S,1:ndim), wCT(ixI^S,1:nw)
942 double precision, intent(inout) :: w(ixI^S,1:nw)
943 type(rc_fluid), intent(in) :: fl
944 double precision, intent(out) :: res(ixI^S)
945
946 double precision :: pth(ixI^S),rho(ixI^S),Rfactor(ixI^S),L1,Tlocal2
947 double precision :: Te(ixI^S)
948 double precision :: Lmax
949 double precision :: Y1, Y2
950 double precision :: de, emax,fact
951 double precision :: Rfloor, Rnew, Rstate
952 double precision :: pfloor, ptarget, pstate
953 double precision :: eint_old, eint_floor, eint_target
954 integer :: ix^D
955
956 call fl%get_pthermal_equi(wct,x,ixi^l,ixo^l,pth)
957 if (associated(fl%get_eps_derivative_from_T) .and. &
958 .not. fl%has_eion_table) then
959 call mpistop("eion exact-cooling table is not initialized")
960 end if
961 call fl%get_rho_equi(wct,x,ixi^l,ixo^l,rho)
962 call fl%get_temperature_equi(wct,x,ixi^l,ixo^l,te)
963 rfactor(ixo^s)=pth(ixo^s)/(rho(ixo^s)*te(ixo^s))
964 res=0d0
965 fact = fl%lref*qdt/fl%tref
966
967 {do ix^db = ixo^lim^db\}
969 fl, rho(ix^d), te(ix^d), rfactor(ix^d), &
970 pstate, eint_old, rstate)
972 fl, rho(ix^d), fl%tlow, rfactor(ix^d), &
973 pfloor, eint_floor, rfloor)
974 lmax = max(zero, (eint_old-eint_floor)/qdt)
975 emax = max(zero, eint_old-eint_floor)
976 if( te(ix^d)<=fl%tcoolmin ) then
977 l1 = zero
978 else if( te(ix^d)>=fl%tcoolmax )then
979 call calc_l_extended(te(ix^d), l1, fl)
980 l1 = l1*rho(ix^d)**2
981 if (phys_trac) then
982 if (te(ix^d) < block%wextra(ix^d,fl%Tcoff_)) then
983 l1 = l1*sqrt((te(ix^d)/block%wextra(ix^d,fl%Tcoff_))**5)
984 end if
985 end if
986 l1 = min(l1,lmax)
987 res(ix^d) = l1*qdt
988 else
989 if (fl%has_eion_table) then
990 call findy_eion(te(ix^d), y1, fl)
991 y2 = y1 + fact*rho(ix^d)
992 call findt_eion(tlocal2, y2, fl)
993 else
994 call findy(te(ix^d),y1,fl)
995 y2 = y1 + fact*rho(ix^d)*rc_gamma_1
996 call findt(tlocal2,y2,fl)
997 end if
998 if(tlocal2<=fl%tcoolmin) then
999 de = emax
1000 else
1002 fl, rho(ix^d), tlocal2, rfactor(ix^d), &
1003 ptarget, eint_target, rnew)
1004 de = eint_old-eint_target
1005 end if
1006
1007 if (phys_trac) then
1008 if (te(ix^d) < block%wextra(ix^d,fl%Tcoff_)) then
1009 de = de*sqrt((te(ix^d)/block%wextra(ix^d,fl%Tcoff_))**5)
1010 end if
1011 end if
1012 de = min(max(zero, de), emax)
1013 res(ix^d) = de
1014 endif
1015 {end do\}
1016 end subroutine get_cool_equi
1017
1018 subroutine cooling_get_rfactor_t(fl, T, Rold, Rnew)
1019 type(rc_fluid), intent(in) :: fl
1020 double precision, intent(in) :: T, Rold
1021 double precision, intent(out) :: Rnew
1022
1023 if (associated(fl%get_Rfactor_from_temperature)) then
1024 call fl%get_Rfactor_from_temperature(t, rnew)
1025 else
1026 rnew = rold
1027 end if
1028 end subroutine cooling_get_rfactor_t
1029
1030 subroutine cooling_get_pthermal_rfactor(fl, rho, T, Rold, pnew, Rnew)
1031 type(rc_fluid), intent(in) :: fl
1032 double precision, intent(in) :: rho, T, Rold
1033 double precision, intent(out) :: pnew, Rnew
1034
1035 if (associated(fl%get_pthermal_Rfactor_from_rho_T)) then
1036 call fl%get_pthermal_Rfactor_from_rho_T(rho, t, pnew, rnew)
1037 else
1038 call cooling_get_rfactor_t(fl, t, rold, rnew)
1039 pnew = rho*rnew*t
1040 end if
1041 end subroutine cooling_get_pthermal_rfactor
1042
1044 fl, rho, T, Rold, pnew, eint_new, Rnew)
1045 type(rc_fluid), intent(in) :: fl
1046 double precision, intent(in) :: rho, T, Rold
1047 double precision, intent(out) :: pnew, eint_new, Rnew
1048
1049 if (associated(fl%get_pthermal_eint_Rfactor_from_rho_T)) then
1050 call fl%get_pthermal_eint_Rfactor_from_rho_T( &
1051 rho, t, pnew, eint_new, rnew)
1052 else
1054 fl, rho, t, rold, pnew, rnew)
1055 eint_new = pnew*invgam
1056 end if
1058
1059 subroutine cooling_get_eint( &
1060 fl, w, x, ixI^L, ixO^L, pthermal, eint)
1062
1063 type(rc_fluid), intent(in) :: fl
1064 integer, intent(in) :: ixI^L, ixO^L
1065 double precision, intent(in) :: w(ixI^S,1:nw)
1066 double precision, intent(in) :: x(ixI^S,1:ndim)
1067 double precision, intent(in) :: pthermal(ixI^S)
1068 double precision, intent(out) :: eint(ixI^S)
1069
1070 if (associated(fl%get_eint)) then
1071 call fl%get_eint(w, x, ixi^l, ixo^l, eint)
1072 else
1073 eint(ixo^s) = pthermal(ixo^s)*invgam
1074 end if
1075 end subroutine cooling_get_eint
1076
1077 subroutine cool_exact(qdt,ixI^L,ixO^L,wCT,wCTprim,w,x,fl)
1079 integer, intent(in) :: ixI^L, ixO^L
1080 double precision, intent(in) :: qdt, x(ixI^S,1:ndim), wCT(ixI^S,1:nw), wCTprim(ixI^S,1:nw)
1081 double precision, intent(inout) :: w(ixI^S,1:nw)
1082 type(rc_fluid), intent(in) :: fl
1083 double precision :: Y1, Y2
1084 double precision :: L1, Tlocal2
1085 double precision :: Rguess, Rfloor, Rnew, R2
1086 double precision :: rho(ixI^S), Te(ixI^S), rhonew(ixI^S)
1087 double precision :: eint_old(ixI^S), eint_work(ixI^S)
1088 double precision :: eint_after(ixI^S)
1089 double precision :: Lmax, fact
1090 double precision :: de_thin, de_thick, emax
1091 double precision :: T1, T2, Tnew(ixI^S), tau, xi
1092 double precision :: xi_arr(ixI^S), emax_rem_arr(ixI^S)
1093 double precision :: cool_fac, fip_prim, frac_lowFIP, fip_factor
1094 double precision :: pold(ixI^S), pwork(ixI^S), pafter(ixI^S)
1095 double precision :: pfloor, ptarget, eint_floor, eint_target
1096 integer :: ix^D
1097
1098 if (associated(fl%get_eps_derivative_from_T) .and. &
1099 .not. fl%has_eion_table) then
1100 call mpistop("eion exact-cooling table is not initialized")
1101 end if
1102 call fl%get_rho(w,x,ixi^l,ixo^l,rhonew)
1103 call fl%get_rho(wct, x, ixi^l, ixo^l, rho)
1104 call fl%get_pthermal(w, x, ixi^l, ixo^l, pwork)
1105 call fl%get_pthermal(wct, x, ixi^l, ixo^l, pold)
1106 call fl%get_temperature(wct, x, ixi^l, ixo^l, te)
1107 call cooling_get_eint( &
1108 fl, wct, x, ixi^l, ixo^l, pold, eint_old)
1109 call cooling_get_eint( &
1110 fl, w, x, ixi^l, ixo^l, pwork, eint_work)
1111 fact = fl%lref*qdt/fl%tref
1112 xi_arr = one
1113 emax_rem_arr = zero
1114 {do ix^db = ixo^lim^db\}
1115 ! Do not apply radiative or Newton updates below the cooling-table cutoff.
1116 ! A hard temperature floor, when enabled, is applied by floortemperature.
1117 if (te(ix^d) <= fl%tcoolmin) cycle
1118 if (rho(ix^d) > zero .and. te(ix^d) > zero) then
1119 rguess = pold(ix^d)/(rho(ix^d)*te(ix^d))
1120 else
1121 rguess = one
1122 end if
1124 fl, rhonew(ix^d), fl%tlow, rguess, &
1125 pfloor, eint_floor, rfloor)
1126 lmax = max(zero, eint_work(ix^d)-eint_floor)/qdt
1127 emax = max(zero, eint_work(ix^d)-eint_floor)
1128 if (fl%rad_newton) then
1129 xi = exp(-pwork(ix^d) / fl%rad_newton_pthick)
1130 xi = min(max(xi, zero), one)
1131 else
1132 xi = one
1133 end if
1134 cool_fac = xi
1135 if (fl%rad_newton) xi_arr(ix^d) = xi
1136
1137 ! ------- (A) OPTICALLY-THIN PART --------
1138 ! --- FIP factor with T-dependent r(T) ---
1139 if (fl%fip_ > 0) then
1140 fip_prim = min(maxfip, max(minfip, wctprim(ix^d,fl%fip_)))
1141 ! frac_lowFIP(T) in [0,1]: low-FIP contribution to total Lambda at T
1142 frac_lowfip = lowfip_fraction(te(ix^d), fl)
1143 ! Effective multiplicative factor on Lambda(T) for this time step:
1144 ! Lambda_eff(T) = g_fip * Lambda(T), with g_fip frozen at T^n
1145 fip_factor = one - frac_lowfip + fip_prim * frac_lowfip
1146 cool_fac = cool_fac * fip_factor
1147 end if
1148 ! --- geometric damping of optically thin radiative losses ---
1149 ! Suppress optically thin cooling near the lower boundary (chromosphere/photosphere)
1150 ! using a phenomenological Gaussian height-dependent damping.
1151 {^ifoned
1152 if (slab_uniform .and. fl%rad_damp .and. x(ix^d,ndim) <= xprobmin1 + fl%rad_damp_height) then
1153 cool_fac = cool_fac * exp(-(x(ix^d,ndim)-xprobmin1-fl%rad_damp_height)**2/fl%rad_damp_scale**2)
1154 end if
1155 if (fl%rc_is_1d_loop .and. slab_uniform .and. fl%rad_damp &
1156 .and. x(ix^d,ndim) >= xprobmax1 - fl%rad_damp_height) then
1157 cool_fac = cool_fac * exp(-(x(ix^d,ndim)-xprobmax1+fl%rad_damp_height)**2/fl%rad_damp_scale**2)
1158 end if
1159 }
1160 {^iftwod
1161 if (slab_uniform .and. fl%rad_damp .and. x(ix^d,ndim) <= xprobmin2 + fl%rad_damp_height) then
1162 cool_fac = cool_fac * exp(-(x(ix^d,ndim)-xprobmin2-fl%rad_damp_height)**2/fl%rad_damp_scale**2)
1163 end if
1164 }
1165 {^ifthreed
1166 if (slab_uniform .and. fl%rad_damp .and. x(ix^d,ndim) <= xprobmin3 + fl%rad_damp_height) then
1167 cool_fac = cool_fac * exp(-(x(ix^d,ndim)-xprobmin3-fl%rad_damp_height)**2/fl%rad_damp_scale**2)
1168 end if
1169 }
1170 ! If the temperature is higher than the maximum table value,
1171 ! assume Bremsstrahlung and cool explicitly.
1172 if (te(ix^d) >= fl%tcoolmax) then
1173 call calc_l_extended(te(ix^d), l1, fl)
1174 l1 = l1 * rho(ix^d)**2
1175 l1 = min(cool_fac * l1, lmax)
1176 de_thin = l1*qdt
1177 w(ix^d,fl%e_) = w(ix^d,fl%e_) - de_thin
1178 else
1179 ! FIP and damping are frozen multiplicative factors in Lambda_eff
1180 ! during this source update.
1181 if (fl%has_eion_table) then
1182 call findy_eion(te(ix^d), y1, fl)
1183 y2 = y1 + cool_fac*fact*rho(ix^d)
1184 call findt_eion(tlocal2, y2, fl)
1185 else
1186 ! For a pressure-dependent EOS, this one-dimensional Townsend map
1187 ! remains approximate. Endpoint energy is nevertheless mapped
1188 ! consistently through pthermal(rho,Tlocal2).
1189 call findy(te(ix^d), y1, fl)
1190 y2 = y1 + cool_fac*fact*rho(ix^d)*rc_gamma_1
1191 call findt(tlocal2, y2, fl)
1192 end if
1193 ! Convert delta_T to an energy loss delta_e, respecting internal-energy floor.
1194 if (tlocal2 <= fl%tcoolmin) then
1195 de_thin = emax
1196 else
1198 fl, rho(ix^d), tlocal2, rguess, &
1199 ptarget, eint_target, rnew)
1200 de_thin = eint_old(ix^d)-eint_target
1201 end if
1202 ! --- TRAC modification: approximate, NOT strictly EI ---
1203 ! This rescaling is applied *after* the EI step and depends on T^n,
1204 ! so it does not correspond to integrating dT/dt with a modified
1205 ! Lambda(T). Kept here for practical reason, but should be
1206 ! understood as an approximate correction on top of EI.
1207 if (phys_trac) then
1208 if (te(ix^d) < block%wextra(ix^d,fl%Tcoff_)) then
1209 de_thin = de_thin * sqrt( (te(ix^d)/block%wextra(ix^d,fl%Tcoff_))**5 )
1210 end if
1211 end if
1212 de_thin = min(max(zero, de_thin), emax)
1213 w(ix^d,fl%e_) = w(ix^d,fl%e_) - de_thin
1214 end if
1215 if (fl%rad_newton) then
1216 emax_rem_arr(ix^d) = max(zero, emax - de_thin)
1217 end if
1218 {end do\}
1219
1220 ! ------- (B) OPTICALLY-THICK (NEWTON) PART --------
1221 if (fl%rad_newton) then
1222 call fl%get_temperature(w, x, ixi^l, ixo^l, tnew)
1223 call fl%get_pthermal(w, x, ixi^l, ixo^l, pafter)
1224 call cooling_get_eint( &
1225 fl, w, x, ixi^l, ixo^l, pafter, eint_after)
1226 {do ix^db = ixo^lim^db\}
1227 if (te(ix^d) <= fl%tcoolmin) cycle
1228 t1 = tnew(ix^d)
1229 tau = max(0.1d0 * sqrt(fl%rad_newton_rhosurf/rho(ix^d)), 4.d0*qdt)
1230 t2 = fl%rad_newton_trad + (t1-fl%rad_newton_trad)*exp(-qdt/tau)
1231 if (rho(ix^d) > zero .and. tnew(ix^d) > zero) then
1232 rguess = pafter(ix^d)/(rho(ix^d)*tnew(ix^d))
1233 else
1234 rguess = one
1235 end if
1237 fl, rho(ix^d), t2, rguess, &
1238 ptarget, eint_target, r2)
1239 de_thick = (one-xi_arr(ix^d)) * &
1240 (eint_after(ix^d)-eint_target)
1241 ! Only cap cooling. Negative de_thick represents Newton heating.
1242 de_thick = min(de_thick, emax_rem_arr(ix^d))
1243 w(ix^d,fl%e_) = w(ix^d,fl%e_) - de_thick
1244 {end do\}
1245 end if
1246 end subroutine cool_exact
1247
1248 subroutine calc_l_extended (tpoint, lpoint,fl)
1249 ! Calculate l for t beyond tcoolmax
1250 ! Assumes Bremsstrahlung for the interpolated tables
1251 ! Uses the power law for piecewise power laws
1252 double precision, intent(IN) :: tpoint
1253 double precision, intent(OUT) :: lpoint
1254 type(rc_fluid), intent(in) :: fl
1255
1256 if(fl%isPPL) then
1257 lpoint =fl%l_PPL(fl%n_PPL) * ( tpoint / fl%t_PPL(fl%n_PPL) )**fl%a_PPL(fl%n_PPL)
1258 else
1259 lpoint = fl%Lcool(fl%ncool) * sqrt( tpoint / fl%tcoolmax)
1260 end if
1261 end subroutine calc_l_extended
1262
1263 double precision function lowfip_fraction(tpoint, fl)
1265
1266 double precision, intent(in) :: tpoint
1267 type(rc_fluid), intent(in) :: fl
1268
1269 double precision :: lgtp
1270 integer :: jl
1271
1272 if (tpoint <= fl%tcool(1)) then
1273 lowfip_fraction = fl%frac_lowFIP(1)
1274 return
1275 else if (tpoint >= fl%tcool(fl%ncool)) then
1276 lowfip_fraction = fl%frac_lowFIP(fl%ncool)
1277 return
1278 end if
1279
1280 lgtp = dlog10(tpoint)
1281 jl = int((lgtp - fl%lgtcoolmin) / fl%lgstep) + 1
1282 jl = max(1, min(fl%ncool-1, jl))
1283
1284 lowfip_fraction = fl%frac_lowFIP(jl) &
1285 + (tpoint - fl%tcool(jl)) &
1286 * (fl%frac_lowFIP(jl+1) - fl%frac_lowFIP(jl)) &
1287 / (fl%tcool(jl+1) - fl%tcool(jl))
1288 end function lowfip_fraction
1289
1290 subroutine findl (tpoint,Lpoint,fl)
1291 ! Fast search option to find correct point
1292 ! in cooling curve
1294
1295 double precision,intent(IN) :: tpoint
1296 double precision, intent(OUT) :: Lpoint
1297 type(rc_fluid), intent(in) :: fl
1298
1299 double precision :: lgtp
1300 integer :: jl,i
1301
1302 if(fl%isPPL) then
1303 i = maxloc(fl%t_PPL, dim=1, mask=fl%t_PPL<tpoint)
1304 lpoint = fl%l_PPL(i) * (tpoint / fl%t_PPL(i))**fl%a_PPL(i)
1305 else
1306 lgtp = dlog10(tpoint)
1307 jl = int((lgtp - fl%lgtcoolmin) /fl%lgstep) + 1
1308 lpoint = fl%Lcool(jl)+ (tpoint-fl%tcool(jl)) &
1309 * (fl%Lcool(jl+1)-fl%Lcool(jl)) &
1310 / (fl%tcool(jl+1)-fl%tcool(jl))
1311 end if
1312
1313 end subroutine findl
1314
1315 ! The Townsend transform uses a one-dimensional Y(T) table. For a
1316 ! pressure-dependent EOS this gives an approximate temperature update,
1317 ! because the strict heat capacity depends on density. The endpoint
1318 ! energy is nevertheless mapped consistently through
1319 ! pthermal(rho,Tlocal2). A strict Y(T,rho) treatment is deferred.
1320 subroutine findy (tpoint,Ypoint,fl)
1321 ! Fast search option to find correct point in cooling time
1323
1324 double precision,intent(IN) :: tpoint
1325 double precision, intent(OUT) :: Ypoint
1326 type(rc_fluid), intent(in) :: fl
1327
1328 double precision :: lgtp
1329 double precision :: y_extra,factor
1330 integer :: jl,i
1331
1332 if(fl%isPPL) then
1333 i = maxloc(fl%t_PPL, dim=1, mask=fl%t_PPL<tpoint)
1334 factor = fl%l_PPL(fl%n_PPL+1) * fl%t_PPL(i) / (fl%l_PPL(i) * fl%t_PPL(fl%n_PPL+1))
1335 if(fl%a_PPL(i)==1.d0) then
1336 y_extra = log( fl%t_PPL(i) / tpoint )
1337 else
1338 y_extra = 1 / (1 - fl%a_PPL(i)) * (1 - ( fl%t_PPL(i) / tpoint )**(fl%a_PPL(i)-1) )
1339 end if
1340 ypoint = fl%y_PPL(i) + factor*y_extra
1341 else
1342 lgtp = dlog10(tpoint)
1343 jl = int((lgtp - fl%lgtcoolmin) / fl%lgstep) + 1
1344 ypoint = fl%Yc(jl)+ (tpoint-fl%tcool(jl)) &
1345 * (fl%Yc(jl+1)-fl%Yc(jl)) &
1346 / (fl%tcool(jl+1)-fl%tcool(jl))
1347 end if
1348
1349 end subroutine findy
1350
1351 subroutine findy_eion(tpoint, Ypoint, fl)
1353 double precision, intent(in) :: tpoint
1354 double precision, intent(out) :: Ypoint
1355 type(rc_fluid), intent(in) :: fl
1356
1357 double precision :: lgtp
1358 integer :: jl
1359
1360 if (.not. fl%has_eion_table) then
1361 call mpistop("eion cooling transform table is not initialized")
1362 end if
1363 if (tpoint <= fl%Teion(1)) then
1364 ypoint = fl%Yeion(1)
1365 else if (tpoint >= fl%Teion(fl%ncool)) then
1366 ypoint = fl%Yeion(fl%ncool)
1367 else
1368 lgtp = dlog10(tpoint)
1369 jl = int((lgtp-fl%eion_lgtmin)/fl%eion_lgstep) + 1
1370 jl = max(1, min(fl%ncool-1, jl))
1371 ypoint = fl%Yeion(jl) + &
1372 (tpoint-fl%Teion(jl)) * &
1373 (fl%Yeion(jl+1)-fl%Yeion(jl)) / &
1374 (fl%Teion(jl+1)-fl%Teion(jl))
1375 end if
1376 end subroutine findy_eion
1377
1378 subroutine findt (tpoint,Ypoint,fl)
1379 ! Fast search option to find correct temperature
1380 ! from temporal evolution function. Only possible this way because T is a monotonously
1381 ! decreasing function for the interpolated tables
1382 ! Uses eq. A7 from Townsend 2009 for piecewise power laws
1384
1385 double precision,intent(OUT) :: tpoint
1386 double precision, intent(IN) :: Ypoint
1387 type(rc_fluid), intent(in) :: fl
1388
1389 double precision :: factor
1390 integer :: jl,jc,jh,i
1391
1392 if(fl%isPPL) then
1393 i = minloc(fl%y_PPL, dim=1, mask=fl%y_PPL>ypoint)
1394 factor = fl%l_PPL(i) * fl%t_PPL(fl%n_PPL+1) / (fl%l_PPL(fl%n_PPL+1) * fl%t_PPL(i))
1395 if(fl%a_PPL(i)==1.d0) then
1396 tpoint = fl%t_PPL(i) * exp( -1.d0 * factor * ( ypoint - fl%y_PPL(i)))
1397 else
1398 tpoint = fl%t_PPL(i) * (1 - (1 - fl%a_PPL(i)) * factor * (ypoint - fl%y_PPL(i)))**(1 / (1 - fl%a_PPL(i)))
1399 end if
1400 else
1401 if(ypoint >= fl%Yc(1)) then
1402 tpoint = fl%tcoolmin
1403 else if (ypoint == fl%Yc(fl%ncool)) then
1404 tpoint = fl%tcoolmax
1405 else
1406 jl=0
1407 jh=fl%ncool+1
1408 do
1409 if(jh-jl <= 1) exit
1410 jc=(jh+jl)/2
1411 if(ypoint <= fl%Yc(jc)) then
1412 jl=jc
1413 else
1414 jh=jc
1415 end if
1416 end do
1417 ! Linear interpolation to obtain correct temperature
1418 tpoint = fl%tcool(jl)+ (ypoint-fl%Yc(jl)) &
1419 * (fl%tcool(jl+1)-fl%tcool(jl)) &
1420 / (fl%Yc(jl+1)-fl%Yc(jl))
1421 end if
1422 end if
1423 end subroutine findt
1424
1425 subroutine findt_eion(tpoint, Ypoint, fl)
1426 type(rc_fluid), intent(in) :: fl
1427 double precision, intent(out) :: tpoint
1428 double precision, intent(in) :: Ypoint
1429
1430 integer :: jl, jc, jh
1431
1432 if (.not. fl%has_eion_table) then
1433 call mpistop("eion cooling transform table is not initialized")
1434 end if
1435 if (ypoint >= fl%Yeion(1)) then
1436 tpoint = fl%Teion(1)
1437 else if (ypoint <= fl%Yeion(fl%ncool)) then
1438 tpoint = fl%Teion(fl%ncool)
1439 else
1440 jl = 0
1441 jh = fl%ncool+1
1442 do
1443 if (jh-jl <= 1) exit
1444 jc = (jh+jl)/2
1445 if (ypoint <= fl%Yeion(jc)) then
1446 jl = jc
1447 else
1448 jh = jc
1449 end if
1450 end do
1451 tpoint = fl%Teion(jl) + &
1452 (ypoint-fl%Yeion(jl)) * &
1453 (fl%Teion(jl+1)-fl%Teion(jl)) / &
1454 (fl%Yeion(jl+1)-fl%Yeion(jl))
1455 end if
1456 end subroutine findt_eion
1457
1458end module mod_radiative_cooling
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
This module contains definitions of global parameters and variables and some generic functions/subrou...
type(state), pointer block
Block pointer for using one block and its previous state.
integer, parameter unitpar
file handle for IO
character(len=std_len), dimension(:), allocatable par_files
Which par files are used as input.
logical phys_trac
Use TRAC for MHD or 1D HD.
logical slab_uniform
uniform Cartesian geometry or not (stretched Cartesian)
This module defines the procedures of a physics module. It contains function pointers for the various...
Definition mod_physics.t:4
module radiative cooling – add optically thin radiative cooling
subroutine getvar_cooling(ixil, ixol, w, x, coolrate, fl)
subroutine findt_eion(tpoint, ypoint, fl)
subroutine cooling_get_eint(fl, w, x, ixil, ixol, pthermal, eint)
subroutine radiative_cooling_init_params(phys_gamma, he_abund)
Radiative cooling initialization.
subroutine findl(tpoint, lpoint, fl)
subroutine get_cool_equi(qdt, ixil, ixol, wct, w, x, fl, res)
subroutine cooling_get_rfactor_t(fl, t, rold, rnew)
subroutine findy_eion(tpoint, ypoint, fl)
subroutine radiative_cooling_init(fl, read_params)
subroutine cooling_get_pthermal_rfactor(fl, rho, t, rold, pnew, rnew)
subroutine radiative_cooling_build_eion_table(fl)
subroutine radiative_cooling_add_source(qdt, ixil, ixol, wct, wctprim, w, x, qsourcesplit, active, fl)
subroutine calc_l_extended(tpoint, lpoint, fl)
double precision function lowfip_fraction(tpoint, fl)
subroutine cooling_get_pthermal_eint_rfactor(fl, rho, t, rold, pnew, eint_new, rnew)
subroutine cool_exact(qdt, ixil, ixol, wct, wctprim, w, x, fl)
subroutine findt(tpoint, ypoint, fl)
subroutine floortemperature(qdt, ixil, ixol, wct, w, x, fl)
subroutine getvar_cooling_exact(qdt, ixil, ixol, wct, w, x, coolrate, fl)
subroutine findy(tpoint, ypoint, fl)
module containing all optically thin radiative cooling tables
double precision, dimension(1:101) l_dere_corona
double precision, dimension(1:71) t_mlsolar1
double precision, dimension(1:151) l_cl_solar
double precision, dimension(1:5) t_fm
double precision, dimension(1:14) a_spex_dm_fine
double precision, dimension(1:9) a_rosner
double precision, dimension(1:110) l_spex
double precision, dimension(1:51) l_mb
double precision, dimension(1:10) t_rosner
double precision, dimension(1:5) a_hildner
double precision, dimension(1:9) x_rosner
double precision, dimension(1:7) x_klimchuk
double precision, dimension(1:151) l_cl_ism
double precision, dimension(1:8) t_spex_dm_rough
double precision, dimension(1:110) nenh_spex
double precision, dimension(1:110) t_spex
double precision, dimension(1:76) l_dm_2
double precision, dimension(1:15) t_spex_dm_fine
double precision, dimension(1:7) x_spex_dm_rough
double precision, dimension(1:14) x_spex_dm_fine
double precision, dimension(1:71) l_mlsolar1
double precision, dimension(1:45) t_jccorona
double precision, dimension(1:5) x_hildner
double precision, dimension(1:71) t_mlcosmol
double precision, dimension(1:151) t_cl_ism
double precision, dimension(1:151) t_cl_solar
double precision, dimension(1:51) t_mb
double precision, dimension(1:8) t_klimchuk
double precision, dimension(1:55) t_colgan
double precision, dimension(1:55) l_colgan
double precision, dimension(1:4) a_fm
double precision, dimension(1:101) l_dere_photo
double precision, dimension(1:45) l_jccorona
double precision, dimension(1:71) l_mlwc
double precision, dimension(1:71) l_dm
double precision, dimension(1:71) t_mlwc
double precision, dimension(1:7) a_spex_dm_rough
double precision, dimension(1:71) t_dm
double precision, dimension(1:7) a_klimchuk
double precision, dimension(1:71) l_mlcosmol
double precision, dimension(1:76) t_dm_2
double precision, dimension(1:6) t_hildner
double precision, dimension(1:4) x_fm
double precision, dimension(1:101) t_dere
double precision, dimension(1:101) lowfip_frac