-
Notifications
You must be signed in to change notification settings - Fork 0
/
reionization.f90
373 lines (278 loc) · 12.7 KB
/
reionization.f90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
module Reionization
use Precision
use AMLutils
implicit none
!This module puts smooth tanh reionization of specified mid-point (z_{re}) and width
!The tanh function is in the variable (1+z)**Rionization_zexp
!Rionization_zexp=1.5 has the property that for the same z_{re}
!the optical depth agrees with infinitely sharp model for matter domination
!So tau and zre can be mapped into each other easily (for any symmetric window)
!However for generality the module maps tau into z_{re} using a binary search
!so could be easily modified for other monatonic parameterizations.
!AL March 2008
!AL July 2008 - added trap for setting optical depth without use_optical_depth
!See CAMB notes for further discussion: http://cosmologist.info/notes/CAMB.pdf
character(LEN=*), parameter :: Reionization_Name = 'CAMB_reionization'
real(dl), parameter :: Reionization_DefFraction = -1._dl
!if -1 set from YHe assuming Hydrogen and first ionization of Helium follow each other
real(dl) :: Reionization_AccuracyBoost = 1._dl
real(dl) :: Rionization_zexp = 1.5_dl
logical :: include_helium_fullreion = .true.
type ReionizationParams
logical :: Reionization
logical :: use_optical_depth
real(dl) :: redshift, delta_redshift, fraction
real(dl) :: optical_depth
!Parameters for the second reionization of Helium
real(dl) :: helium_redshift = 3.5_dl
real(dl) :: helium_delta_redshift = 0.5
real(dl) :: helium_redshiftstart = 5._dl
end type ReionizationParams
type ReionizationHistory
!These two are used by main code to bound region where xe changing
real(dl) :: tau_start, tau_complete
!This is set from main code
real(dl) :: akthom, fHe
!The rest are internal to this module.
real(dl) :: WindowVarMid, WindowVarDelta
end type ReionizationHistory
real(dl), parameter :: Reionization_maxz = 50._dl
real(dl), private, parameter :: Reionization_tol = 1d-5
real(dl), private, external :: dtauda, rombint,rombint2
Type(ReionizationParams), private, pointer :: ThisReion
Type(ReionizationHistory), private, pointer :: ThisReionHist
contains
function Reionization_xe(a, tau, xe_recomb)
!a and time tau and redundant, both provided for convenience
!xe_recomb is xe(tau_start) from recombination (typically very small, ~2e-4)
!xe should map smoothly onto xe_recomb
real(dl), intent(in) :: a
real(dl), intent(in), optional :: tau, xe_recomb
real(dl) Reionization_xe
real(dl) tgh, xod
real(dl) xstart
if (present(xe_recomb)) then
xstart = xe_recomb
else
xstart = 0._dl
end if
xod = (ThisReionHist%WindowVarMid - 1._dl/a**Rionization_zexp)/ThisReionHist%WindowVarDelta
if (xod > 100) then
tgh=1.d0
else
tgh=tanh(xod)
end if
Reionization_xe =(ThisReion%fraction-xstart)*(tgh+1._dl)/2._dl+xstart
if (include_helium_fullreion .and. a > (1/(1+ ThisReion%helium_redshiftstart))) then
!Effect of Helium becoming fully ionized is small so details not important
xod = (1+ThisReion%helium_redshift - 1._dl/a)/ThisReion%helium_delta_redshift
if (xod > 100) then
tgh=1.d0
else
tgh=tanh(xod)
end if
Reionization_xe = Reionization_xe + ThisReionHist%fHe*(tgh+1._dl)/2._dl
end if
end function Reionization_xe
function Reionization_timesteps(ReionHist)
!minimum number of time steps to use between tau_start and tau_complete
!Scaled by AccuracyBoost later
!steps may be set smaller than this anyway
Type(ReionizationHistory) :: ReionHist
integer Reionization_timesteps
Reionization_timesteps = 50
end function Reionization_timesteps
subroutine Reionization_ReadParams(Reion, Ini)
use IniFile
Type(ReionizationParams) :: Reion
Type(TIniFile) :: Ini
Reion%Reionization = Ini_Read_Logical_File(Ini,'reionization')
if (Reion%Reionization) then
Reion%use_optical_depth = Ini_Read_Logical_File(Ini,'re_use_optical_depth')
if (Reion%use_optical_depth) then
Reion%optical_depth = Ini_Read_Double_File(Ini,'re_optical_depth')
else
Reion%redshift = Ini_Read_Double_File(Ini,'re_redshift')
end if
Reion%delta_redshift = Ini_Read_Double_File(Ini,'re_delta_redshift', 0.5_dl) !default similar to CMBFAST original
Reion%fraction = Ini_Read_Double_File(Ini,'re_ionization_frac',Reionization_DefFraction)
Reion%helium_redshift = Ini_Read_Double_File(Ini,'re_helium_redshift', 3.5_dl)
Reion%helium_delta_redshift = Ini_Read_Double_File(Ini,'re_helium_delta_redshift', 0.5_dl)
Reion%helium_redshiftstart = Ini_Read_Double_File(Ini,'re_helium_redshiftstart', &
Reion%helium_redshift + 3*Reion%helium_delta_redshift)
end if
end subroutine Reionization_ReadParams
subroutine Reionization_SetParamsForZre(Reion,ReionHist)
Type(ReionizationParams), target :: Reion
Type(ReionizationHistory), target :: ReionHist
ReionHist%WindowVarMid = (1._dl+Reion%redshift)**Rionization_zexp
ReionHist%WindowVarDelta = &
Rionization_zexp*(1._dl+Reion%redshift)**(Rionization_zexp-1._dl)*Reion%delta_redshift
end subroutine Reionization_SetParamsForZre
subroutine Reionization_Init(Reion, ReionHist, Yhe, akthom, tau0, FeedbackLevel)
use constants
use errors
Type(ReionizationParams), target :: Reion
Type(ReionizationHistory), target :: ReionHist
real(dl), intent(in) :: akthom, tau0, Yhe
integer, intent(in) :: FeedbackLevel
real(dl) astart
ReionHist%akthom = akthom
ReionHist%fHe = YHe/(mass_ratio_He_H*(1.d0-YHe))
ReionHist%tau_start=tau0
ReionHist%tau_complete=tau0
ThisReion => Reion
ThisReionHist => ReionHist
if (Reion%Reionization) then
if (Reion%optical_depth /= 0._dl .and. .not. Reion%use_optical_depth) &
write (*,*) 'WARNING: You seem to have set the optical depth, but use_optical_depth = F'
if (Reion%use_optical_depth.and.Reion%optical_depth<0.001 &
.or. .not.Reion%use_optical_depth .and. Reion%Redshift<0.001) then
Reion%Reionization = .false.
end if
end if
if (Reion%Reionization) then
if (Reion%fraction==Reionization_DefFraction) &
Reion%fraction = 1._dl + ReionHist%fHe !H + singly ionized He
if (Reion%use_optical_depth) then
call Reionization_SetFromOptDepth(Reion,ReionHist)
if (global_error_flag/=0) return
if (FeedbackLevel > 0) write(*,'("Reion redshift = ",f6.3)') Reion%redshift
end if
call Reionization_SetParamsForZre(ThisReion,ThisReionHist)
!this is a check, agrees very well in default parameterization
if (FeedbackLevel > 1) write(*,'("Integrated opt depth = ",f7.4)') &
Reionization_GetOptDepth(Reion, ReionHist)
!Get relevant times
astart=1.d0/(1.d0+Reion%redshift + Reion%delta_redshift*8)
ReionHist%tau_start = max(0.05_dl, rombint(dtauda,0._dl,astart,1d-3))
!Time when a very small reionization fraction (assuming tanh fitting)
ReionHist%tau_complete = min(tau0, &
ReionHist%tau_start+ rombint(dtauda,astart,1.d0/(1.d0+max(0.d0,Reion%redshift-Reion%delta_redshift*8)),1d-3))
end if
end subroutine Reionization_Init
subroutine Reionization_SetDefParams(Reion)
Type(ReionizationParams) :: Reion
Reion%Reionization = .true.
Reion%use_optical_depth = .false.
Reion%optical_depth = 0._dl
Reion%redshift = 10
Reion%fraction = Reionization_DefFraction
Reion%delta_redshift = 0.5_dl
Reion%helium_redshift = 3.5_dl
Reion%helium_delta_redshift = 0.5
Reion%helium_redshiftstart = 5._dl
end subroutine Reionization_SetDefParams
subroutine Reionization_Validate(Reion, OK)
Type(ReionizationParams),intent(in) :: Reion
logical, intent(inout) :: OK
if (Reion%Reionization) then
if (Reion%use_optical_depth) then
if (Reion%optical_depth<0 .or. Reion%optical_depth > 0.9 .or. &
include_helium_fullreion .and. Reion%optical_depth<0.01) then
OK = .false.
write(*,*) 'Optical depth is strange. You have:', Reion%optical_depth
end if
else
if (Reion%redshift < 0 .or. Reion%Redshift +Reion%delta_redshift*3 > Reionization_maxz .or. &
include_helium_fullreion .and. Reion%redshift < Reion%helium_redshift) then
OK = .false.
write(*,*) 'Reionization redshift strange. You have: ',Reion%Redshift
end if
end if
if (Reion%fraction/= Reionization_DefFraction .and. (Reion%fraction < 0 .or. Reion%fraction > 1.5)) then
OK = .false.
write(*,*) 'Reionization fraction strange. You have: ',Reion%fraction
end if
if (Reion%delta_redshift > 3 .or. Reion%delta_redshift<0.1 ) then
!Very narrow windows likely to cause problems in interpolation etc.
!Very broad likely to conflict with quasar data at z=6
OK = .false.
write(*,*) 'Reionization delta_redshift is strange. You have: ',Reion%delta_redshift
end if
end if
end subroutine Reionization_Validate
function Reionization_doptdepth_dz(z)
real(dl) :: Reionization_doptdepth_dz
real(dl), intent(in) :: z
real(dl) a
a = 1._dl/(1._dl+z)
Reionization_doptdepth_dz = Reionization_xe(a)*ThisReionHist%akthom*dtauda(a)
end function Reionization_doptdepth_dz
function Reionization_GetOptDepth(Reion, ReionHist)
Type(ReionizationParams), target :: Reion
Type(ReionizationHistory), target :: ReionHist
real(dl) Reionization_GetOptDepth
ThisReion => Reion
ThisReionHist => ReionHist
Reionization_GetOptDepth = rombint2(Reionization_doptdepth_dz,0.d0,Reionization_maxz,&
Reionization_tol, 20, nint(Reionization_maxz/Reion%delta_redshift*5))
end function Reionization_GetOptDepth
subroutine Reionization_zreFromOptDepth(Reion, ReionHist)
!General routine to find zre parameter given optical depth
use Errors
Type(ReionizationParams) :: Reion
Type(ReionizationHistory) :: ReionHist
real(dl) try_b, try_t
real(dl) tau
integer i
try_b = 0
try_t = Reionization_maxz
i=0
do
i=i+1
Reion%redshift = (try_t + try_b)/2
call Reionization_SetParamsForZre(Reion,ReionHist)
tau = Reionization_GetOptDepth(Reion, ReionHist)
if (tau > Reion%optical_depth) then
try_t = Reion%redshift
else
try_b = Reion%redshift
end if
if (abs(try_b - try_t) < 2e-3/Reionization_AccuracyBoost) exit
if (i>100) call GlobalError('Reionization_zreFromOptDepth: failed to converge',error_reionization)
end do
if (abs(tau - Reion%optical_depth) > 0.002 .and. global_error_flag==0) then
write (*,*) 'Reionization_zreFromOptDepth: Did not converge to optical depth'
write (*,*) 'tau =',tau, 'optical_depth = ', Reion%optical_depth
write (*,*) try_t, try_b
write (*,*) '(If running a chain, have you put a constraint on tau?)'
call GlobalError('Reionization did not converge to optical depth',error_reionization)
end if
end subroutine Reionization_zreFromOptDepth
subroutine Reionization_SetFromOptDepth(Reion, ReionHist)
Type(ReionizationParams) :: Reion
Type(ReionizationHistory) :: ReionHist
! This subroutine calculates the redshift of reionization
! This implementation is approximate but quite accurate and fast
real(dl) dz, optd
real(dl) z, tmp, tmpHe
integer na
Reion%redshift = 0
if (Reion%Reionization .and. Reion%optical_depth /= 0) then
!Do binary search to find zre from z
!This is general method
call Reionization_zreFromOptDepth(Reion, ReionHist)
if (.false.) then
!Use equivalence with sharp for special case
optd=0
na=1
dz=1._dl/2000/Reionization_AccuracyBoost
tmp = dz*Reion%fraction*ThisReionHist%akthom
tmpHe = dz*(Reion%fraction+ReionHist%fHe)*ThisReionHist%akthom
z=0
do while (optd < Reion%optical_depth)
z=na*dz
if (include_helium_fullreion .and. z < Reion%helium_redshift) then
optd=optd+ tmpHe*dtauda(1._dl/(1._dl+z))
else
optd=optd+tmp*dtauda(1._dl/(1._dl+z))
end if
na=na+1
end do
end if
else
Reion%Reionization = .false.
end if
end subroutine Reionization_SetFromOptDepth
end module Reionization