MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_cak_force.t
Go to the documentation of this file.
1!> Module to include CAK radiation line force in (magneto)hydrodynamic models
2!> Computes both the force from free electrons and the force from an ensemble of
3!> lines (various possibilities for the latter).
4!> There is an option to only simulate the pure radial CAK force (with various
5!> corrections applied) as well as the full vector CAK force. Depending on the
6!> chosen option additional output are the CAK line force component(s) and,
7!> when doing a 1-D radial force, the finite disc factor.
8!>
9!> USAGE:
10!>
11!> 1. Include a cak_list in the .par file and activate (m)hd_cak_force in the
12!> (m)hd_list
13!> 2. Create a mod_usr.t file for the problem with appropriate initial and
14!> boundary conditions
15!> 3. In the mod_usr.t header call the mod_cak_force module to have access to
16!> global variables from mod_cak_force, which may be handy for printing or
17!> the computation of other variables inside mod_usr.t
18!> 4. In usr_init of mod_usr.t call the set_cak_force_norm routine and pass
19!> along the stellar radius and wind temperature---this is needed to
20!> correctly compute the (initial) force normalisation inside mod_cak_force
21!> 5. Always ensure that the set_cak_force_norm routine is called in mod_usr.t
22! either in usr_init or in usr_set_parameters and after the hydro unit
23! variables have been set/computed
24!>
25!> Developed by Florian Driessen (2022, 2026)
27
30
31 implicit none
32 private
33
34 !> Line-ensemble parameters in the Gayley (1995) formalism
35 real(8), public :: cak_alpha, gayley_qbar, gayley_q0
36
37 !> Free-electron scattering opacity
38 real(8), public :: kappae_cgs = 0.0d0
39
40 !> Ray positions + weights for impact parameter and azimuthal radiation angle
41 real(8), allocatable :: ay(:), wy(:), aphi(:), wphi(:)
42
43 !> The adiabatic index
44 real(8) :: cak_gamma
45
46 !> Variables needed to compute force normalisation fnorm in initialisation
47 real(8) :: lstar_cgs, lstar, rstar, kappae, clight
48
49 !> To enforce a floor temperature (~wind temperature) for adiabatic (M)HD
50 real(8) :: tfloor
51
52 ! Telectron/Teff = 0.8 from Puls+ (2000), A&AS 141; assume Twind = Telectron
53 real(8), parameter :: ratio_twind_teff = 0.8d0
54
55 !> Method and option for CAK force
56 integer :: method_cakforce, type_cak_1d
57
58 !> Type CAK line force method
59 integer, parameter :: radialforce=0
60 integer, parameter :: vectorforce=1
61
62 !> Type 1-D CAK line force options
63 integer, parameter :: pointstar=0
64 integer, parameter :: fdisc=1
65 integer, parameter :: fdisc_cutoff=2
66
67 !> Amount of rays in radiation polar and radiation azimuthal direction
68 integer :: nthetaray, nphiray
69
70 !> Extra slots to store quantities in w-array
71 integer, public :: gcak1_, gcak2_, gcak3_, fdf_
72 integer, public :: alpha_, qbar_, q0_, kappae_
73
74 !> To treat source term in split or unsplit (default) fashion
75 logical :: cak_split=.false.
76
77 !> To activate the pure radial vector CAK line force computation
78 logical :: fix_vector_force_1d=.false.
79
80 !> To compute CAK line-force parameters from opacity tables
81 logical :: use_cak_table=.false.
82
83 !> Allow reading different opacity table than default src/tables/CAK_tables
84 !> If true, name_cak_table requires absolute/relative path to file location
85 logical :: use_custom_cak_table=.false.
86
87 !> String to specify the CAK force method: 'radial' or 'vector'
88 character(len=6) :: cak_force_method
89
90 !> String to choose between the 1-D CAK line force options
91 !> Can be 'pointstar', 'finitedisc', or 'finitedisc_cutoff'
92 character(len=256) :: cak_1d_type
93
94 !> String of the opacity table to use from src/tables/CAK_tables
95 character(len=256) :: name_cak_table=''
96
97 !> Public methods for mod_hd_phys or mod_mhd_phys
98 public :: cak_init
99 public :: cak_add_source
100 public :: cak_get_dt
101
102 !> Public method for mod_usr
103 public :: set_cak_force_norm
104
105contains
106
107 !> Read this module's parameters from a file
108 subroutine cak_params_read(files)
110 use mod_comm_lib, only: mpistop
111
112 character(len=*), intent(in) :: files(:)
113
114 ! Local variable
115 integer :: n
116
117 namelist /cak_list/ cak_alpha, gayley_qbar, gayley_q0, kappae_cgs, &
118 cak_force_method, cak_1d_type, cak_split, &
119 nphiray, nthetaray, fix_vector_force_1d, &
120 use_cak_table, name_cak_table, use_custom_cak_table
121
122 do n = 1,size(files)
123 open(unitpar, file=trim(files(n)), status="old")
124 read(unitpar, cak_list, end=111)
125 111 close(unitpar)
126 enddo
127
128 select case(trim(cak_force_method))
129 case('radial')
130 method_cakforce = radialforce
131 case('vector')
132 method_cakforce = vectorforce
133 case default
134 write(unitterm,*) 'cak_force_method = ', trim(cak_force_method)
135 call mpistop('cak_params_read: unknown CAK force in cak_list')
136 end select
137
138 ! Set CAK method for pure radial force
139 select case(trim(cak_1d_type))
140 case('pointstar')
141 type_cak_1d = pointstar
142 case('finitedisc')
143 type_cak_1d = fdisc
144 case('finitedisc_cutoff')
145 type_cak_1d = fdisc_cutoff
146 case default
147 write(unitterm,*) 'cak_1d_type = ', trim(cak_1d_type)
148 call mpistop('cak_params_read: unknown CAK wind method in cak_list')
149 end select
150
151 end subroutine cak_params_read
152
153 !> Initialize the module
154 subroutine cak_init(phys_gamma)
156 use mod_comm_lib, only: mpistop
157
158 real(8), intent(in) :: phys_gamma
159
160 cak_gamma = phys_gamma
161
162 ! Set some defaults when user does not
163 cak_alpha = 0.65d0
164 gayley_qbar = 2000.0d0
165 gayley_q0 = 2000.0d0
166
167 cak_force_method = 'radial'
168 cak_1d_type = 'finitedisc'
169
170 nthetaray = 6
171 nphiray = 6
172
173 call cak_params_read(par_files)
174
175 if (method_cakforce == radialforce) then
176 gcak1_ = var_set_extravar("gcak1", "gcak1")
177 fdf_ = var_set_extravar("fdfac", "fdfac")
178 endif
179
180 if (method_cakforce == vectorforce) then
181 gcak1_ = var_set_extravar("gcak1", "gcak1")
182 gcak2_ = var_set_extravar("gcak2", "gcak2")
183 gcak3_ = var_set_extravar("gcak3", "gcak3")
184 call rays_init(nthetaray,nphiray)
185 endif
186
187 if (cak_split) any_source_split = .true.
188
189 if (use_cak_table) then
190 call init_cak_table(trim(name_cak_table), use_custom_cak_table)
191
192 select case(trim(name_cak_table))
193 ! Electron opacity ~= 0.2*(1+X) [cgs] for a fully-ionised plasma with X
194 ! the hydrogen mass fraction
195 case('Y02400')
196 kappae_cgs = 0.34d0
197 case('Y09800')
198 kappae_cgs = 0.2d0
199 end select
200
201 alpha_ = var_set_extravar("alpha", "alpha")
202 qbar_ = var_set_extravar("Qbar", "Qbar")
203 q0_ = var_set_extravar("Q0", "Q0")
204 kappae_ = var_set_extravar("kappae", "kappae")
205 else
206 alpha_ = var_set_wextra()
207 qbar_ = var_set_wextra()
208 q0_ = var_set_wextra()
209 kappae_ = var_set_wextra()
210 endif
211
212 ! Some sanity checks
213 if (si_unit .and. use_cak_table) then
214 call mpistop('cak_init: SI_unit=T but LTE tables assume cgs units')
215 endif
216
217 if (slab) then
218 call mpistop('cak_init: Cartesian geometry not supported')
219 endif
220
221 if ((cak_alpha < 0.0d0) .or. (cak_alpha >= 1.0d0)) then
222 call mpistop('cak_init: input alpha in [0,1[')
223 endif
224
225 if ((gayley_qbar < 0.0d0) .or. (gayley_q0 < 0.0d0)) then
226 call mpistop('cak_init: input Qbar or Q0 is < 0')
227 endif
228
229 if (method_cakforce == vectorforce .and. ndir < 3) then
230 call mpistop('cak_init: vector CAK force only for 2.5D and 3D')
231 endif
232
233 if (kappae_cgs < smalldouble) then
234 call mpistop('cak_init: set input kappae to a reasonable constant')
235 endif
236
237 end subroutine cak_init
238
239 !> Compute some (unitless) variables for CAK force normalisation
240 subroutine set_cak_force_norm(rstar_cgs,twind_cgs)
242 use mod_constants
243
244 real(8), intent(in) :: rstar_cgs, twind_cgs
245
246 lstar_cgs = 4.0d0*dpi * rstar_cgs**2.0d0 * sigma_sb_cgs * twind_cgs**4.0d0
247
248 ! Dimensionless quantities used in this module computations
250 clight = const_c/unit_velocity
251 lstar = lstar_cgs/(unit_density * unit_length**5.0d0 / unit_time**3.0d0)
252 rstar = rstar_cgs/unit_length
253 tfloor = ratio_twind_teff * twind_cgs/unit_temperature
254
255 end subroutine set_cak_force_norm
256
257 !> w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO
258 subroutine cak_add_source(qdt,ixI^L,ixO^L,wCT,w,x,energy,qsourcesplit,active)
260 use mod_comm_lib, only: mpistop
261
262 integer, intent(in) :: ixi^l, ixo^l
263 real(8), intent(in) :: qdt, x(ixi^s,1:ndim), wct(ixi^s,1:nw)
264 real(8), intent(inout) :: w(ixi^s,1:nw)
265 logical, intent(in) :: energy, qsourcesplit
266 logical, intent(inout) :: active
267
268 ! Local variables
269 integer :: idir, ix^d
270 real(8) :: gcak(ixo^s,1:3), ge(ixo^s), ptherm(ixi^s), pmin(ixi^s)
271 real(8) :: local_rho_cgs, local_twind_cgs
272 real(8) :: table_alpha, table_qbar, table_q0, table_kappae_cgs
273
274 ! By default add source in unsplit fashion together with the fluxes
275 if (qsourcesplit .eqv. cak_split) then
276
277 active = .true.
278
279 if (use_cak_table) then
280 ! Set line-statistic parameters from local density and wind temperature
281 {do ix^db = ixo^lim^db\}
282 local_rho_cgs = wct(ix^d,iw_rho)*unit_density
283 local_twind_cgs = tfloor*unit_temperature
284
285 call set_cak_opacity(local_rho_cgs,local_twind_cgs, &
286 table_alpha,table_qbar,table_q0,table_kappae_cgs)
287
288 w(ix^d,alpha_) = table_alpha
289 w(ix^d,qbar_) = table_qbar
290 w(ix^d,q0_) = table_q0
291 w(ix^d,kappae_) = table_kappae_cgs
292 {enddo^d&\}
293
294 ! Convert electron opacity from table to unitless
295 w(ixo^s,kappae_) = w(ixo^s,kappae_) * unit_density * unit_length
296
297 else
298 ! Constant line-statistic parameters
299 block%wextra(ixo^s,alpha_) = cak_alpha
300 block%wextra(ixo^s,qbar_) = gayley_qbar
301 block%wextra(ixo^s,q0_) = gayley_q0
302 block%wextra(ixo^s,kappae_) = kappae
303 endif
304
305 ! Thomson force
306 call get_gelectron(ixi^l,ixo^l,w,x,ge)
307
308 ! CAK line force
309 select case(method_cakforce)
310 case(radialforce)
311 call get_cak_force_radial(ixi^l,ixo^l,wct,w,x,gcak)
312 case(vectorforce)
313 call get_cak_force_vector(ixi^l,ixo^l,wct,w,x,gcak)
314 end select
315
316 ! Update conservative vars: w = w + qdt*gsource
317 do idir = 1,ndir
318 if (idir == 1) gcak(ixo^s,idir) = gcak(ixo^s,idir) + ge(ixo^s)
319
320 w(ixo^s,iw_mom(idir)) = w(ixo^s,iw_mom(idir)) &
321 + qdt * gcak(ixo^s,idir) * wct(ixo^s,iw_rho)
322 enddo
323
324 if (energy) then
325 w(ixo^s,iw_e) = w(ixo^s,iw_e) &
326 + qdt * sum(gcak(ixo^s,1:ndir) * wct(ixo^s,iw_mom(1:ndir)))
327
328 ! Impose fixed floor temperature to mimic stellar heating
329 call phys_get_pthermal(w,x,ixi^l,ixo^l,ptherm)
330 pmin(ixo^s) = w(ixo^s,iw_rho) * tfloor
331
332 where (ptherm(ixo^s) < pmin(ixo^s))
333 w(ixo^s,iw_e) = w(ixo^s,iw_e) &
334 + (pmin(ixo^s) - ptherm(ixo^s)) / (cak_gamma - 1.0d0)
335 endwhere
336 endif
337 endif
338
339 end subroutine cak_add_source
340
341 !> 1-D CAK line force in the Gayley line-ensemble distribution parametrisation
342 subroutine get_cak_force_radial(ixI^L,ixO^L,wCT,w,x,gcak)
344 use mod_comm_lib, only: mpistop
345
346 integer, intent(in) :: ixi^l, ixo^l
347 real(8), intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
348 real(8), intent(inout) :: w(ixi^s,1:nw)
349 real(8), intent(out) :: gcak(ixo^s,1:3)
350
351 ! Local variables
352 real(8) :: vr(ixi^s), dvrdr(ixo^s)
353 real(8) :: beta_fd(ixo^s), fdfac(ixo^s), taus(ixo^s), ge(ixo^s)
354 real(8) :: alpha(ixo^s), qbar(ixo^s), q0(ixo^s), kappae(ixo^s)
355
356 if (use_cak_table) then
357 alpha(ixo^s) = w(ixo^s,alpha_)
358 qbar(ixo^s) = w(ixo^s,qbar_)
359 q0(ixo^s) = w(ixo^s,q0_)
360 kappae(ixo^s) = w(ixo^s,kappae_)
361 else
362 alpha(ixo^s) = block%wextra(ixo^s,alpha_)
363 qbar(ixo^s) = block%wextra(ixo^s,qbar_)
364 q0(ixo^s) = block%wextra(ixo^s,q0_)
365 kappae(ixo^s) = block%wextra(ixo^s,kappae_)
366 endif
367
368 vr(ixi^s) = wct(ixi^s,iw_mom(1)) / wct(ixi^s,iw_rho)
369 call get_velocity_gradient(ixi^l,ixo^l,vr,x,1,dvrdr)
370
371 if (physics_type == 'hd') then
372 ! Monotonic flow to avoid multiple resonances and radiative coupling
373 dvrdr(ixo^s) = max(abs(dvrdr(ixo^s)), smalldouble)
374 elseif (physics_type == 'mhd') then
375 ! Allow material to fallback to the star in a magnetosphere model
376 dvrdr(ixo^s) = max(dvrdr(ixo^s), smalldouble)
377 endif
378
379 ! Thomson force
380 call get_gelectron(ixi^l,ixo^l,w,x,ge)
381
382 ! Sobolev optical depth for line ensemble (tau = Qbar * t_r) and the force
383 select case (type_cak_1d)
384 case(pointstar, fdisc)
385 taus(ixo^s) = qbar(ixo^s) * kappae(ixo^s) * clight &
386 * wct(ixo^s,iw_rho) / dvrdr(ixo^s)
387 gcak(ixo^s,1) = qbar(ixo^s) / (1.0d0 - alpha(ixo^s)) * ge(ixo^s) &
388 / taus(ixo^s)**alpha(ixo^s)
389
390 case(fdisc_cutoff)
391 taus(ixo^s) = q0(ixo^s) * kappae(ixo^s) * clight &
392 * wct(ixo^s,iw_rho) / dvrdr(ixo^s)
393 gcak(ixo^s,1) = qbar(ixo^s) * ge(ixo^s) / (1.0d0 - alpha(ixo^s)) &
394 * ( (1.0d0 + taus(ixo^s))**(1.0d0 - alpha(ixo^s)) - 1.0d0 ) &
395 / taus(ixo^s)
396 end select
397
398 ! Finite disk factor parameterisation (Owocki & Puls 1996)
399 beta_fd(ixo^s) = ( 1.0d0 - vr(ixo^s)/(x(ixo^s,1) * dvrdr(ixo^s)) ) &
400 * (rstar/x(ixo^s,1))**2.0d0
401
402 select case (type_cak_1d)
403 case(pointstar)
404 fdfac(ixo^s) = 1.0d0
405 case(fdisc, fdisc_cutoff)
406 where (beta_fd(ixo^s) >= 1.0d0)
407 fdfac(ixo^s) = 1.0d0/(1.0d0 + alpha(ixo^s))
408 elsewhere (beta_fd(ixo^s) < -1.0d10)
409 fdfac(ixo^s) = abs(beta_fd(ixo^s))**alpha(ixo^s) &
410 / (1.0d0 + alpha(ixo^s))
411 elsewhere (abs(beta_fd(ixo^s)) > 1.0d-3)
412 fdfac(ixo^s) = &
413 (1.0d0 - (1.0d0 - beta_fd(ixo^s))**(1.0d0 + alpha(ixo^s))) &
414 / (beta_fd(ixo^s)*(1.0d0 + alpha(ixo^s)))
415 elsewhere
416 fdfac(ixo^s) = 1.0d0 - 0.5d0*alpha(ixo^s)*beta_fd(ixo^s) &
417 * ( 1.0d0 + 1.0d0/3.0d0 * (1.0d0 - alpha(ixo^s))*beta_fd(ixo^s) )
418 endwhere
419 end select
420
421 ! Correct radial line force for finite disc (if applicable)
422 gcak(ixo^s,1) = gcak(ixo^s,1) * fdfac(ixo^s)
423 gcak(ixo^s,2) = 0.0d0
424 gcak(ixo^s,3) = 0.0d0
425
426 ! Fill the nwextra slots for output
427 w(ixo^s,gcak1_) = gcak(ixo^s,1)
428 w(ixo^s,fdf_) = fdfac(ixo^s)
429
430 end subroutine get_cak_force_radial
431
432 !> Vector CAK line force in the Gayley line-ensemble distribution parametrisation
433 subroutine get_cak_force_vector(ixI^L,ixO^L,wCT,w,x,gcak)
436
437 ! Subroutine arguments
438 integer, intent(in) :: ixi^l, ixo^l
439 real(8), intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
440 real(8), intent(inout) :: w(ixi^s,1:nw)
441 real(8), intent(out) :: gcak(ixo^s,1:3)
442
443 ! Local variables
444 integer :: ix^d, itray, ipray
445 real(8) :: a1, a2, a3, wyray, y, wpray, phiray, wtot, mustar, dvndn
446 real(8) :: costp, sintp, cospp, sinpp, cott0
447 real(8) :: vr(ixi^s), vt(ixi^s), vp(ixi^s), inv_rho(ixi^s), inv_r(ixi^s)
448 real(8) :: vrr(ixi^s), vtr(ixi^s), vpr(ixi^s)
449 real(8) :: dvrdr(ixo^s), dvtdr(ixo^s), dvpdr(ixo^s)
450 real(8) :: dvrdt(ixo^s), dvtdt(ixo^s), dvpdt(ixo^s)
451 real(8) :: dvrdp(ixo^s), dvtdp(ixo^s), dvpdp(ixo^s)
452 real(8) :: gcaktmp1, gcaktmp2, gcaktmp3, taus, integrand
453 real(8) :: alpha(ixo^s), qbar(ixo^s), q0(ixo^s), kappae(ixo^s)
454
455 if (use_cak_table) then
456 alpha(ixo^s) = w(ixo^s,alpha_)
457 qbar(ixo^s) = w(ixo^s,qbar_)
458 q0(ixo^s) = w(ixo^s,q0_)
459 kappae(ixo^s) = w(ixo^s,kappae_)
460 else
461 alpha(ixo^s) = block%wextra(ixo^s,alpha_)
462 qbar(ixo^s) = block%wextra(ixo^s,qbar_)
463 q0(ixo^s) = block%wextra(ixo^s,q0_)
464 kappae(ixo^s) = block%wextra(ixo^s,kappae_)
465 endif
466
467 inv_rho(ixi^s) = 1.0d0/wct(ixi^s,iw_rho)
468 inv_r(ixi^s) = 1.0d0/x(ixi^s,1)
469
470 ! Initialisation to have full velocity strain tensor expression at all times
471 vt(ixo^s) = 0.0d0; vtr(ixo^s) = 0.0d0
472 vp(ixo^s) = 0.0d0; vpr(ixo^s) = 0.0d0
473 cott0 = 0.0d0
474 dvrdr(ixo^s) = 0.0d0; dvtdr(ixo^s) = 0.0d0; dvpdr(ixo^s) = 0.0d0
475 dvrdt(ixo^s) = 0.0d0; dvtdt(ixo^s) = 0.0d0; dvpdt(ixo^s) = 0.0d0
476 dvrdp(ixo^s) = 0.0d0; dvtdp(ixo^s) = 0.0d0; dvpdp(ixo^s) = 0.0d0
477
478 ! Populate velocity field(s) depending on dimensions and directions
479 vr(ixi^s) = wct(ixi^s,iw_mom(1)) * inv_rho(ixi^s)
480 vrr(ixi^s) = vr(ixi^s) * inv_r(ixi^s)
481
482 {^nooned
483 vt(ixi^s) = wct(ixi^s,iw_mom(2)) * inv_rho(ixi^s)
484 vtr(ixi^s) = vt(ixi^s) * inv_r(ixi^s)
485
486 if (ndir > 2) then
487 vp(ixi^s) = wct(ixi^s,iw_mom(3)) * inv_rho(ixi^s)
488 vpr(ixi^s) = vp(ixi^s) * inv_r(ixi^s)
489 endif
490 }
491
492 ! Derivatives of velocity field in each coordinate direction (r=1,t=2,p=3)
493 call get_velocity_gradient(ixi^l,ixo^l,vr,x,1,dvrdr)
494
495 {^nooned
496 call get_velocity_gradient(ixi^l,ixo^l,vr,x,2,dvrdt)
497 call get_velocity_gradient(ixi^l,ixo^l,vt,x,1,dvtdr)
498 call get_velocity_gradient(ixi^l,ixo^l,vt,x,2,dvtdt)
499
500 if (ndir > 2) then
501 call get_velocity_gradient(ixi^l,ixo^l,vp,x,1,dvpdr)
502 call get_velocity_gradient(ixi^l,ixo^l,vp,x,2,dvpdt)
503 endif
504 }
505 {^ifthreed
506 call get_velocity_gradient(ixi^l,ixo^l,vr,x,3,dvrdp)
507 call get_velocity_gradient(ixi^l,ixo^l,vt,x,3,dvtdp)
508 call get_velocity_gradient(ixi^l,ixo^l,vp,x,3,dvpdp)
509 }
510
511 ! Get total acceleration from all rays at a certain grid point
512 {do ix^db=ixomin^db,ixomax^db\}
513 gcaktmp1 = 0.0d0
514 gcaktmp2 = 0.0d0
515 gcaktmp3 = 0.0d0
516
517 ! Loop over the rays; first theta then phi radiation angle
518 ! Get weights from current ray and their position
519 do itray = 1,nthetaray
520 wyray = wy(itray)
521 y = ay(itray)
522
523 do ipray = 1,nphiray
524 wpray = wphi(ipray)
525 phiray = aphi(ipray)
526
527 ! Redistribute the phi rays by a small offset
528 ! if (mod(itp,3) == 1) then
529 ! phip = phip + dphi/3.0d0
530 ! elseif (mod(itp,3) == 2) then
531 ! phip = phip - dphi/3.0d0
532 ! endif
533
534 ! === Geometrical factors ===
535 ! Make y quadrature linear in mu, not mu**2; better for gtheta,gphi
536 ! y -> mu quadrature is preserved; y=0 <=> mu=1; y=1 <=> mu=mustar
537 mustar = sqrt(max(1.0d0 - (rstar*inv_r(ix^d))**2.0d0, 0.0d0))
538 costp = 1.0d0 - y*(1.0d0 - mustar)
539 sintp = sqrt(max(1.0d0 - costp*costp, 0.0d0))
540 sinpp = sin(phiray)
541 cospp = cos(phiray)
542 {^nooned cott0 = cos(x(ix^d,2))/max(sin(x(ix^d,2)), smalldouble)}
543
544 ! More weight close to star, less farther away
545 wtot = wyray * wpray * (1.0d0 - mustar)
546
547 ! Convenients a la Cranmer & Owocki (1995), ApJ 440, eq. 42
548 a1 = costp
549 a2 = sintp * cospp
550 a3 = sintp * sinpp
551
552 ! Get total velocity gradient for one ray with given (theta', phi')
553 dvndn = a1*a1 * dvrdr(ix^d) + a2*a2 * (dvtdt(ix^d) + vrr(ix^d)) &
554 + a3*a3 * (dvpdp(ix^d) + cott0 * vtr(ix^d) + vrr(ix^d)) &
555 + a1*a2 * (dvtdr(ix^d) + dvrdt(ix^d) - vtr(ix^d)) &
556 + a1*a3 * (dvpdr(ix^d) + dvrdp(ix^d) - vpr(ix^d)) &
557 + a2*a3 * (dvpdt(ix^d) + dvtdp(ix^d) - cott0 * vpr(ix^d))
558
559 ! No multiple resonances in CAK
560 dvndn = abs(dvndn)
561
562 taus = q0(ix^d) * kappae(ix^d) * clight * wct(ix^d,iw_rho) / dvndn
563 integrand = ((1.0d0 + taus)**(1.0d0 - alpha(ix^d)) - 1.0d0) / taus
564
565 ! Convert gradient back from wind coordinates (r',theta',phi') to
566 ! stellar coordinates (r,theta,phi)
567 gcaktmp1 = gcaktmp1 + integrand * a1 * wtot
568 gcaktmp2 = gcaktmp2 + integrand * a2 * wtot
569 gcaktmp3 = gcaktmp3 + integrand * a3 * wtot
570 enddo
571 enddo
572
573 gcak(ix^d,1:3) = [gcaktmp1, gcaktmp2, gcaktmp3] &
574 * kappae(ix^d) * qbar(ix^d) / (1.0d0 - alpha(ix^d))
575 {enddo\}
576
577 ! Normalisation for line force array
578 ! NOTE: extra 1/pi factor comes from integration in radiation Phi angle
579 gcak = gcak/dpi * lstar/(4.0d0*dpi*rstar**2.0d0 * clight)
580
581 if (fix_vector_force_1d) then
582 gcak(ixo^s,2) = 0.0d0
583 gcak(ixo^s,3) = 0.0d0
584 endif
585
586 ! Fill the nwextra slots for output
587 w(ixo^s,gcak1_) = gcak(ixo^s,1)
588 w(ixo^s,gcak2_) = gcak(ixo^s,2)
589 w(ixo^s,gcak3_) = gcak(ixo^s,3)
590
591 end subroutine get_cak_force_vector
592
593 !> Compute continuum radiation force from Thomson scattering
594 subroutine get_gelectron(ixI^L,ixO^L,w,x,ge)
596
597 integer, intent(in) :: ixi^l, ixo^l
598 real(8), intent(in) :: w(ixi^s,1:nw), x(ixi^s,1:ndim)
599 real(8), intent(out):: ge(ixo^s)
600
601 real(8) :: kappae(ixo^s)
602
603 if (use_cak_table) then
604 kappae(ixo^s) = w(ixo^s,kappae_)
605 else
606 kappae(ixo^s) = block%wextra(ixo^s,kappae_)
607 endif
608
609 ge(ixo^s) = kappae(ixo^s) * lstar/(4.0d0*dpi * clight * x(ixo^s,1)**2.0d0)
610
611 end subroutine get_gelectron
612
613 !> Check time step for total radiation contribution
614 subroutine cak_get_dt(wprim,ixI^L,ixO^L,dtnew,dx^D,x)
616
617 integer, intent(in) :: ixi^l, ixo^l
618 real(8), intent(in) :: dx^d, x(ixi^s,1:ndim)
619 real(8), intent(in) :: wprim(ixi^s,1:nw)
620 real(8), intent(inout) :: dtnew
621
622 ! Local variables
623 real(8) :: ge(ixo^s), max_gr, dt_cak
624
625 call get_gelectron(ixi^l,ixo^l,wprim,x,ge)
626
627 dtnew = bigdouble
628
629 ! Get dt from line force that is saved in the w-array in nwextra slot
630 max_gr = max( maxval(abs(ge(ixo^s) + wprim(ixo^s,gcak1_))), epsilon(1.0d0) )
631 dt_cak = minval( sqrt(block%dx(ixo^s,1)/max_gr) )
632 dtnew = min(dtnew, courantpar*dt_cak)
633
634 {^nooned
635 if (method_cakforce == vectorforce) then
636 max_gr = max( maxval(abs(wprim(ixo^s,gcak2_))), epsilon(1.0d0) )
637 dt_cak = minval( sqrt(block%dx(ixo^s,1) * block%dx(ixo^s,2)/max_gr) )
638 dtnew = min(dtnew, courantpar*dt_cak)
639
640 {^ifthreed
641 max_gr = max( maxval(abs(wprim(ixo^s,gcak3_))), epsilon(1.0d0) )
642 dt_cak = minval( sqrt(block%dx(ixo^s,1) * sin(block%dx(ixo^s,3))/max_gr) )
643 dtnew = min(dtnew, courantpar*dt_cak)
644 }
645 endif
646 }
647
648 end subroutine cak_get_dt
649
650 !> Compute velocity gradient in direction 'idir' on a non-uniform grid
651 subroutine get_velocity_gradient(ixI^L,ixO^L,v,x,idir,grad_vn)
653
654 integer, intent(in) :: ixi^l, ixo^l, idir
655 real(8), intent(in) :: v(ixi^s), x(ixi^s,1:ndim)
656 real(8), intent(out) :: grad_vn(ixo^s)
657
658 ! Local variables
659 real(8) :: forw(ixo^s), backw(ixo^s), cent(ixo^s)
660 integer :: jrx^l, hrx^l{^nooned,jtx^l, htx^l}{^ifthreed,jpx^l, hpx^l}
661
662 ! Index +1 (j) and index -1 (h) in radial direction; kr(dir,dim)=1, dir=dim
663 jrx^l=ixo^l+kr(1,^d);
664 hrx^l=ixo^l-kr(1,^d);
665
666 {^nooned
667 ! Index +1 (j) and index -1 (h) in polar direction
668 jtx^l=ixo^l+kr(2,^d);
669 htx^l=ixo^l-kr(2,^d);
670 }
671
672 {^ifthreed
673 ! Index +1 (j) and index -1 (h) in azimuthal direction
674 jpx^l=ixo^l+kr(3,^d);
675 hpx^l=ixo^l-kr(3,^d);
676 }
677
678 ! grad(v.n) on non-uniform grid according to Sundqvist & Veronis (1970)
679 select case (idir)
680 case(1) ! Radial forward, backward, and central derivatives
681 forw(ixo^s) = (x(ixo^s,1) - x(hrx^s,1)) * v(jrx^s) &
682 / ((x(jrx^s,1) - x(ixo^s,1)) * (x(jrx^s,1) - x(hrx^s,1)))
683
684 backw(ixo^s) = -(x(jrx^s,1) - x(ixo^s,1)) * v(hrx^s) &
685 / ((x(ixo^s,1) - x(hrx^s,1)) * (x(jrx^s,1) - x(hrx^s,1)))
686
687 cent(ixo^s) = (x(jrx^s,1) + x(hrx^s,1) - 2.0d0*x(ixo^s,1)) * v(ixo^s) &
688 / ((x(ixo^s,1) - x(hrx^s,1)) * (x(jrx^s,1) - x(ixo^s,1)))
689 {^nooned
690 case(2) ! Polar forward, backward, and central derivatives
691 forw(ixo^s) = (x(ixo^s,2) - x(htx^s,2)) * v(jtx^s) &
692 / (x(ixo^s,1) * (x(jtx^s,2) - x(ixo^s,2)) * (x(jtx^s,2) - x(htx^s,2)))
693
694 backw(ixo^s) = -(x(jtx^s,2) - x(ixo^s,2)) * v(htx^s) &
695 / ( x(ixo^s,1) * (x(ixo^s,2) - x(htx^s,2)) * (x(jtx^s,2) - x(htx^s,2)))
696
697 cent(ixo^s) = (x(jtx^s,2) + x(htx^s,2) - 2.0d0*x(ixo^s,2)) * v(ixo^s) &
698 / ( x(ixo^s,1) * (x(ixo^s,2) - x(htx^s,2)) * (x(jtx^s,2) - x(ixo^s,2)))
699 }
700 {^ifthreed
701 case(3) ! Azimuthal forward, backward, and central derivatives
702 forw(ixo^s) = (x(ixo^s,3) - x(hpx^s,3)) * v(jpx^s) &
703 / ( x(ixo^s,1)*sin(x(ixo^s,2)) * (x(jpx^s,3) - x(ixo^s,3)) * (x(jpx^s,3) - x(hpx^s,3)))
704
705 backw(ixo^s) = -(x(jpx^s,3) - x(ixo^s,3)) * v(hpx^s) &
706 / ( x(ixo^s,1)*sin(x(ixo^s,2)) * (x(ixo^s,3) - x(hpx^s,3)) * (x(jpx^s,3) - x(hpx^s,3)))
707
708 cent(ixo^s) = (x(jpx^s,3) + x(hpx^s,3) - 2.0d0*x(ixo^s,3)) * v(ixo^s) &
709 / ( x(ixo^s,1)*sin(x(ixo^s,2)) * (x(ixo^s,3) - x(hpx^s,3)) * (x(jpx^s,3) - x(ixo^s,3)))
710 }
711 end select
712
713 ! Total gradient for given velocity field
714 grad_vn(ixo^s) = backw(ixo^s) + cent(ixo^s) + forw(ixo^s)
715
716 end subroutine get_velocity_gradient
717
718 !> Initialise (theta',phi') radiation angles coming from stellar disc
719 subroutine rays_init(ntheta_point,nphi_point)
721
722 ! Subroutine arguments
723 integer, intent(in) :: ntheta_point, nphi_point
724
725 ! Local variables
726 real(8) :: ymin, ymax, phipmin, phipmax, adum
727 integer :: ii
728
729 ! Minimum and maximum range of theta and phi rays
730 ! NOTE: theta points are cast into y-space
731 ymin = 0.0d0
732 ymax = 1.0d0
733 phipmin = -dpi !0.0d0
734 phipmax = dpi !2.0d0*dpi
735 ! dphi = (phipmax - phipmin) / nphi_point
736
737 if (mype == 0) then
738 allocate(ay(ntheta_point))
739 allocate(wy(ntheta_point))
740 allocate(aphi(nphi_point))
741 allocate(wphi(nphi_point))
742
743 ! theta and phi ray positions and weights: Gauss-Legendre
744 call gauss_legendre_quadrature(ymin,ymax,ntheta_point,ay,wy)
745 call gauss_legendre_quadrature(phipmin,phipmax,nphi_point,aphi,wphi)
746
747 ! theta rays and weights: uniform
748 ! dth = 1.0d0 / nthetap
749 ! adum = ymin + 0.5d0*dth
750 ! do ip = 1,nthetap
751 ! ay(ip) = adum
752 ! wy(ip) = 1.0d0/nthetap
753 ! adum = adum + dth
754 ! !print*,'phipoints'
755 ! !print*,ip,aphi(ip),wphi(ip),dphi
756 ! enddo
757
758 ! phi ray position and weights: uniform
759 ! adum = phipmin + 0.5d0*dphi
760 ! do ii = 1,nphi_point
761 ! aphi(ii) = adum
762 ! wphi(ii) = 1.0d0/nphi_point
763 ! adum = adum + dphi
764 ! enddo
765
766 print*, '==========================='
767 print*, ' Radiation ray setup '
768 print*, '==========================='
769 print*, 'Theta ray points + weights '
770 do ii = 1,ntheta_point
771 print*,ii,ay(ii),wy(ii)
772 enddo
773 print*
774 print*, 'Phi ray points + weights '
775 do ii = 1,nphi_point
776 print*,ii,aphi(ii),wphi(ii)
777 enddo
778 print*
779 endif
780
781 call mpi_barrier(icomm,ierrmpi)
782
783 !===========================
784 ! Broadcast what mype=0 read
785 !===========================
786 if (npe > 1) then
787 if (mype /= 0) then
788 allocate(ay(ntheta_point))
789 allocate(wy(ntheta_point))
790 allocate(aphi(nphi_point))
791 allocate(wphi(nphi_point))
792 endif
793
794 call mpi_bcast(ay,ntheta_point,mpi_double_precision,0,icomm,ierrmpi)
795 call mpi_bcast(wy,ntheta_point,mpi_double_precision,0,icomm,ierrmpi)
796 call mpi_bcast(aphi,nphi_point,mpi_double_precision,0,icomm,ierrmpi)
797 call mpi_bcast(wphi,nphi_point,mpi_double_precision,0,icomm,ierrmpi)
798 endif
799
800 end subroutine rays_init
801
802 !> Fast Gauss-Legendre N-point quadrature algorithm by G. Rybicki
803 subroutine gauss_legendre_quadrature(xlow,xhi,n,x,w)
804 ! Given the lower and upper limits of integration xlow and xhi, and given n,
805 ! this routine returns arrays x and w of length n, containing the abscissas
806 ! and weights of the Gauss-Legendre N-point quadrature
808
809 ! Subroutine arguments
810 real(8), intent(in) :: xlow, xhi
811 integer, intent(in) :: n
812 real(8), intent(out) :: x(n), w(n)
813
814 ! Local variables
815 real(8) :: p1, p2, p3, pp, xl, xm, z, z1
816 real(8), parameter :: error=3.0d-14
817 integer :: i, j, m
818
819 m = (n + 1)/2
820 xm = 0.5d0*(xhi + xlow)
821 xl = 0.5d0*(xhi - xlow)
822
823 do i = 1,m
824 z = cos( dpi * (i - 0.25d0)/(n + 0.5d0) )
825 z1 = 2.0d0 * z
826
827 do while (abs(z1 - z) > error)
828 p1 = 1.0d0
829 p2 = 0.0d0
830
831 do j = 1,n
832 p3 = p2
833 p2 = p1
834 p1 = ( (2.0d0*j - 1.0d0)*z*p2 - (j - 1.0d0)*p3 )/j
835 enddo
836
837 pp = n*(z*p1 - p2) / (z*z - 1.0d0)
838 z1 = z
839 z = z1 - p1/pp
840 enddo
841
842 x(i) = xm - xl*z
843 x(n+1-i) = xm + xl*z
844 w(i) = 2.0d0*xl / ((1.0d0 - z*z) * pp*pp)
845 w(n+1-i) = w(i)
846 enddo
847
848 end subroutine gauss_legendre_quadrature
849
850end module mod_cak_force
Module to include CAK radiation line force in (magneto)hydrodynamic models Computes both the force fr...
subroutine, public cak_add_source(qdt, ixil, ixol, wct, w, x, energy, qsourcesplit, active)
w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO
integer, public alpha_
integer, public qbar_
real(8), public gayley_qbar
real(8), public gayley_q0
real(8), public cak_alpha
Line-ensemble parameters in the Gayley (1995) formalism.
integer, public fdf_
integer, public kappae_
subroutine, public set_cak_force_norm(rstar_cgs, twind_cgs)
Compute some (unitless) variables for CAK force normalisation.
integer, public gcak3_
integer, public q0_
real(8), public kappae_cgs
Free-electron scattering opacity.
subroutine, public cak_get_dt(wprim, ixil, ixol, dtnew, dxd, x)
Check time step for total radiation contribution.
integer, public gcak1_
Extra slots to store quantities in w-array.
integer, public gcak2_
subroutine, public cak_init(phys_gamma)
Initialize the module.
This module reads in CAK line opacities in the Gayley (1995) notation (alpha, Qbar,...
subroutine, public init_cak_table(tabledir, set_custom_tabledir)
This routine is called when the FLD or CAK module is initialised.
subroutine, public set_cak_opacity(rho, temp, alpha_output, qbar_output, q0_output, kappae_output)
This subroutine calculates the opacity for a given temperature-density structure. Opacities are read ...
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Module for physical and numeric constants.
double precision, parameter sigma_sb_cgs
Stefan Boltzmann constant in cgs from (2 pi^5 kB^4)/(15 c^2 h^3).
double precision, parameter dpi
Pi.
double precision, parameter const_c
universal constants as specified in cgs units
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.
double precision unit_time
Physical scaling factor for time.
double precision unit_density
Physical scaling factor for density.
integer, parameter unitpar
file handle for IO
logical any_source_split
if any normal source term is added in split fasion
integer, dimension(3, 3) kr
Kronecker delta tensor.
integer, parameter ndim
Number of spatial dimensions for grid variables.
double precision unit_length
Physical scaling factor for length.
character(len=std_len), dimension(:), allocatable par_files
Which par files are used as input.
integer icomm
The MPI communicator.
integer mype
The rank of the current MPI task.
integer ndir
Number of spatial dimensions (components) for vector variables.
double precision courantpar
The Courant (CFL) number used for the simulation.
integer ierrmpi
A global MPI error return code.
double precision, dimension(:), allocatable, parameter d
logical slab
Cartesian geometry or not.
integer npe
The number of MPI tasks.
double precision unit_velocity
Physical scaling factor for velocity.
integer, parameter unitterm
Unit for standard output.
double precision unit_temperature
Physical scaling factor for temperature.
logical si_unit
Use SI units (.true.) or use cgs units (.false.).
double precision, dimension(:,:), allocatable dx
spatial steps for all dimensions at all levels
This module defines the procedures of a physics module. It contains function pointers for the various...
Definition mod_physics.t:4
procedure(sub_get_pthermal), pointer phys_get_pthermal
Definition mod_physics.t:77
character(len=name_len) physics_type
String describing the physics type of the simulation.
Definition mod_physics.t:47
Module with all the methods that users can customize in AMRVAC.