MPI-AMRVAC  3.0
The MPI - Adaptive Mesh Refinement - Versatile Advection Code
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. Ensure that the order of calls in usr_init is similar as for test problem
22 !> CAKwind_spherical_1D: first reading usr.par list; then set unit scales;
23 !> then call (M)HD_activate; then call set_cak_force_norm. This order avoids
24 !> an incorrect force normalisation and code crash
25 !>
26 !> Developed by Florian Driessen (2022)
29  implicit none
30 
31  !> Line-ensemble parameters in the Gayley (1995) formalism
32  real(8), public :: cak_alpha, gayley_qbar, gayley_q0
33 
34  !> Switch to choose between the 1-D CAK line force options
35  integer :: cak_1d_opt
36 
37  ! Avoid magic numbers in code for 1-D CAK line force option
38  integer, parameter, private :: radstream=0, fdisc=1, fdisc_cutoff=2
39 
40  !> To treat source term in split or unsplit (default) fashion
41  logical :: cak_split=.false.
42 
43  !> To activate the original CAK 1-D line force computation
44  logical :: cak_1d_force=.false.
45 
46  !> To activate the vector CAK line force computation
47  logical :: cak_vector_force=.false.
48 
49  !> To activate the pure radial vector CAK line force computation
50  logical :: fix_vector_force_1d=.false.
51 
52  !> Amount of rays in radiation polar and radiation azimuthal direction
53  integer :: nthetaray, nphiray
54 
55  !> Ray positions + weights for impact parameter and azimuthal radiation angle
56  real(8), allocatable, private :: ay(:), wy(:), aphi(:), wphi(:)
57 
58  !> The adiabatic index
59  real(8), private :: cak_gamma
60 
61  !> Variables needed to compute force normalisation fnorm in initialisation
62  real(8), private :: lum, dlum, drstar, dke, dclight
63 
64  !> To enforce a floor temperature when doing adiabatic (M)HD
65  real(8), private :: tfloor
66 
67  !> Extra slots to store quantities in w-array
68  integer :: gcak1_, gcak2_, gcak3_, fdf_
69 
70  !> Public method
71  public :: set_cak_force_norm
72 
73 contains
74 
75  !> Read this module's parameters from a file
76  subroutine cak_params_read(files)
78 
79  character(len=*), intent(in) :: files(:)
80 
81  ! Local variable
82  integer :: n
83 
84  namelist /cak_list/ cak_alpha, gayley_qbar, gayley_q0, cak_1d_opt, &
87 
88  do n = 1,size(files)
89  open(unitpar, file=trim(files(n)), status="old")
90  read(unitpar, cak_list, end=111)
91  111 close(unitpar)
92  enddo
93 
94  end subroutine cak_params_read
95 
96  !> Initialize the module
97  subroutine cak_init(phys_gamma)
99 
100  real(8), intent(in) :: phys_gamma
101 
102  cak_gamma = phys_gamma
103 
104  ! Set some defaults when user does not
105  cak_alpha = 0.65d0
106  gayley_qbar = 2000.0d0
107  gayley_q0 = 2000.0d0
108  cak_1d_opt = fdisc
109  nthetaray = 6
110  nphiray = 6
111 
113 
114  if (cak_1d_force) then
115  gcak1_ = var_set_extravar("gcak1", "gcak1")
116  fdf_ = var_set_extravar("fdfac", "fdfac")
117  endif
118 
119  if (cak_vector_force) then
120  gcak1_ = var_set_extravar("gcak1", "gcak1")
121  gcak2_ = var_set_extravar("gcak2", "gcak2")
122  gcak3_ = var_set_extravar("gcak3", "gcak3")
124  endif
125 
126  ! Some sanity checks
127  if ((cak_alpha <= 0.0d0) .or. (cak_alpha > 1.0d0)) then
128  call mpistop('CAK error: choose alpha in [0,1[')
129  endif
130 
131  if ((gayley_qbar < 0.0d0) .or. (gayley_q0 < 0.0d0)) then
132  call mpistop('CAK error: chosen Qbar or Q0 is < 0')
133  endif
134 
135  if (cak_1d_force .and. cak_vector_force) then
136  call mpistop('CAK error: choose either 1-D or vector force')
137  endif
138 
139  end subroutine cak_init
140 
141  !> Compute some (unitless) variables for CAK force normalisation
142  subroutine set_cak_force_norm(rstar,twind)
144  use mod_constants
145 
146  real(8), intent(in) :: rstar, twind
147 
148  lum = 4.0d0*dpi * rstar**2.0d0 * const_sigma * twind**4.0d0
150  dclight = const_c/unit_velocity
151  dlum = lum/(unit_density * unit_length**5.0d0 / unit_time**3.0d0)
152  drstar = rstar/unit_length
153  tfloor = twind/unit_temperature
154 
155  end subroutine set_cak_force_norm
156 
157  !> w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO
158  subroutine cak_add_source(qdt,ixI^L,ixO^L,wCT,w,x,energy,qsourcesplit,active)
160 
161  integer, intent(in) :: ixI^L, ixO^L
162  real(8), intent(in) :: qdt, x(ixI^S,1:ndim), wCT(ixI^S,1:nw)
163  real(8), intent(inout) :: w(ixI^S,1:nw)
164  logical, intent(in) :: energy, qsourcesplit
165  logical, intent(inout) :: active
166 
167  ! Local variables
168  integer :: idir
169  real(8) :: gl(ixO^S,1:3), ge(ixO^S), etherm(ixI^S), emin(ixI^S)
170 
171  ! By default add source in unsplit fashion together with the fluxes
172  if (qsourcesplit .eqv. cak_split) then
173 
174  ! Thomson force
175  call get_gelectron(ixi^l,ixo^l,wct,x,ge)
176 
177  ! CAK line force
178  gl(ixo^s,1:3) = 0.0d0
179 
180  if (cak_1d_force) then
181  call get_cak_force_radial(ixi^l,ixo^l,wct,w,x,gl)
182  elseif (cak_vector_force) then
183  call get_cak_force_vector(ixi^l,ixo^l,wct,w,x,gl)
184  else
185  call mpistop("No valid force option")
186  endif
187 
188  ! Update conservative vars: w = w + qdt*gsource
189  do idir = 1,ndir
190  if (idir == 1) gl(ixo^s,idir) = gl(ixo^s,idir) + ge(ixo^s)
191 
192  w(ixo^s,iw_mom(idir)) = w(ixo^s,iw_mom(idir)) &
193  + qdt * gl(ixo^s,idir) * wct(ixo^s,iw_rho)
194 
195  if (energy) then
196  w(ixo^s,iw_e) = w(ixo^s,iw_e) + qdt * gl(ixo^s,idir) * wct(ixo^s,iw_mom(idir))
197 
198  ! Impose fixed floor temperature to mimic stellar heating
199  call phys_get_pthermal(w,x,ixi^l,ixo^l,etherm)
200  etherm(ixo^s) = etherm(ixo^s) / (cak_gamma - 1.0d0)
201  emin(ixo^s) = w(ixo^s,iw_rho)*tfloor / (cak_gamma - 1.0d0)
202 
203  where (etherm < emin)
204  w(ixo^s,iw_e) = w(ixo^s,iw_e) - etherm(ixo^s) + emin(ixo^s)
205  endwhere
206  endif
207  enddo
208  endif
209 
210  end subroutine cak_add_source
211 
212  !> 1-D CAK line force in the Gayley line-ensemble distribution parametrisation
213  subroutine get_cak_force_radial(ixI^L,ixO^L,wCT,w,x,gcak)
215 
216  integer, intent(in) :: ixI^L, ixO^L
217  real(8), intent(in) :: wCT(ixI^S,1:nw), x(ixI^S,1:ndim)
218  real(8), intent(inout) :: w(ixI^S,1:nw)
219  real(8), intent(inout) :: gcak(ixO^S,1:3)
220 
221  ! Local variables
222  real(8) :: vr(ixI^S), dvrdr(ixO^S)
223  real(8) :: beta_fd(ixO^S), fdfac(ixO^S), taus(ixO^S), ge(ixO^S)
224 
225  vr(ixi^s) = wct(ixi^s,iw_mom(1)) / wct(ixi^s,iw_rho)
226  call get_velocity_gradient(ixi^l,ixo^l,vr,x,1,dvrdr)
227 
228  if (physics_type == 'hd') then
229  ! Monotonic flow to avoid multiple resonances and radiative coupling
230  dvrdr(ixo^s) = abs(dvrdr(ixo^s))
231  else
232  ! Allow material to fallback to the star in a magnetosphere model
233  dvrdr(ixo^s) = max(dvrdr(ixo^s), smalldouble)
234  endif
235 
236  ! Thomson force
237  call get_gelectron(ixi^l,ixo^l,wct,x,ge)
238 
239  ! Sobolev optical depth for line ensemble (tau = Qbar * t_r) and the force
240  select case (cak_1d_opt)
241  case(radstream, fdisc)
242  taus(ixo^s) = gayley_qbar * dke * dclight * wct(ixo^s,iw_rho)/dvrdr(ixo^s)
243  gcak(ixo^s,1) = gayley_qbar/(1.0d0 - cak_alpha) &
244  * ge(ixo^s)/taus(ixo^s)**cak_alpha
245 
246  case(fdisc_cutoff)
247  taus(ixo^s) = gayley_q0 * dke * dclight * wct(ixo^s,iw_rho)/dvrdr(ixo^s)
248  gcak(ixo^s,1) = gayley_qbar * ge(ixo^s) &
249  * ( (1.0d0 + taus(ixo^s))**(1.0d0 - cak_alpha) - 1.0d0 ) &
250  / ( (1.0d0 - cak_alpha) * taus(ixo^s) )
251  case default
252  call mpistop("Error in force computation.")
253  end select
254 
255  ! Finite disk factor parameterisation (Owocki & Puls 1996)
256  beta_fd(ixo^s) = ( 1.0d0 - vr(ixo^s)/(x(ixo^s,1) * dvrdr(ixo^s)) ) &
257  * (drstar/x(ixo^s,1))**2.0d0
258 
259  select case (cak_1d_opt)
260  case(radstream)
261  fdfac(ixo^s) = 1.0d0
262  case(fdisc, fdisc_cutoff)
263  where (beta_fd(ixo^s) >= 1.0d0)
264  fdfac(ixo^s) = 1.0d0/(1.0d0 + cak_alpha)
265  elsewhere (beta_fd(ixo^s) < -1.0d10)
266  fdfac(ixo^s) = abs(beta_fd(ixo^s))**cak_alpha / (1.0d0 + cak_alpha)
267  elsewhere (abs(beta_fd) > 1.0d-3)
268  fdfac(ixo^s) = (1.0d0 - (1.0d0 - beta_fd(ixo^s))**(1.0d0 + cak_alpha)) &
269  / (beta_fd(ixo^s)*(1.0d0 + cak_alpha))
270  elsewhere
271  fdfac(ixo^s) = 1.0d0 - 0.5d0*cak_alpha*beta_fd(ixo^s) &
272  * (1.0d0 + 1.0d0/3.0d0 * (1.0d0 - cak_alpha)*beta_fd(ixo^s))
273  endwhere
274  end select
275 
276  ! Correct radial line force for finite disc (if applicable)
277  gcak(ixo^s,1) = gcak(ixo^s,1) * fdfac(ixo^s)
278  gcak(ixo^s,2) = 0.0d0
279  gcak(ixo^s,3) = 0.0d0
280 
281  ! Fill the nwextra slots for output
282  w(ixo^s,gcak1_) = gcak(ixo^s,1)
283  w(ixo^s,fdf_) = fdfac(ixo^s)
284 
285  end subroutine get_cak_force_radial
286 
287  !> Vector CAK line force in the Gayley line-ensemble distribution parametrisation
288  subroutine get_cak_force_vector(ixI^L,ixO^L,wCT,w,x,gcak)
290  use mod_usr_methods
291 
292  ! Subroutine arguments
293  integer, intent(in) :: ixI^L, ixO^L
294  real(8), intent(in) :: wCT(ixI^S,1:nw), x(ixI^S,1:ndim)
295  real(8), intent(inout) :: w(ixI^S,1:nw)
296  real(8), intent(inout) :: gcak(ixO^S,1:3)
297 
298  ! Local variables
299  integer :: ix^D, itray, ipray
300  real(8) :: a1, a2, a3, wyray, y, wpray, phiray, wtot, mustar, dvndn
301  real(8) :: costp, costp2, sintp, cospp, sinpp, cott0
302  real(8) :: vr(ixI^S), vt(ixI^S), vp(ixI^S)
303  real(8) :: vrr(ixI^S), vtr(ixI^S), vpr(ixI^S)
304  real(8) :: dvrdr(ixO^S), dvtdr(ixO^S), dvpdr(ixO^S)
305  real(8) :: dvrdt(ixO^S), dvtdt(ixO^S), dvpdt(ixO^S)
306  real(8) :: dvrdp(ixO^S), dvtdp(ixO^S), dvpdp(ixO^S)
307 
308  ! Initialisation to have full velocity strain tensor expression at all times
309  vt(ixo^s) = 0.0d0; vtr(ixo^s) = 0.0d0
310  vp(ixo^s) = 0.0d0; vpr(ixo^s) = 0.0d0
311  cott0 = 0.0d0
312  dvrdr(ixo^s) = 0.0d0; dvtdr(ixo^s) = 0.0d0; dvpdr(ixo^s) = 0.0d0
313  dvrdt(ixo^s) = 0.0d0; dvtdt(ixo^s) = 0.0d0; dvpdt(ixo^s) = 0.0d0
314  dvrdp(ixo^s) = 0.0d0; dvtdp(ixo^s) = 0.0d0; dvpdp(ixo^s) = 0.0d0
315 
316  ! Populate velocity field(s) depending on dimensions and directions
317  vr(ixi^s) = wct(ixi^s,iw_mom(1)) / wct(ixi^s,iw_rho)
318  vrr(ixi^s) = vr(ixi^s) / x(ixi^s,1)
319 
320  {^nooned
321  vt(ixi^s) = wct(ixi^s,iw_mom(2)) / wct(ixi^s,iw_rho)
322  vtr(ixi^s) = vt(ixi^s) / x(ixi^s,1)
323 
324  if (ndir > 2) then
325  vp(ixi^s) = wct(ixi^s,iw_mom(3)) / wct(ixi^s,iw_rho)
326  vpr(ixi^s) = vp(ixi^s) / x(ixi^s,1)
327  endif
328  }
329 
330  ! Derivatives of velocity field in each coordinate direction (r=1,t=2,p=3)
331  call get_velocity_gradient(ixi^l,ixo^l,vr,x,1,dvrdr)
332 
333  {^nooned
334  call get_velocity_gradient(ixi^l,ixo^l,vr,x,2,dvrdt)
335  call get_velocity_gradient(ixi^l,ixo^l,vt,x,1,dvtdr)
336  call get_velocity_gradient(ixi^l,ixo^l,vt,x,2,dvtdt)
337 
338  if (ndir > 2) then
339  call get_velocity_gradient(ixi^l,ixo^l,vp,x,1,dvpdr)
340  call get_velocity_gradient(ixi^l,ixo^l,vp,x,2,dvpdt)
341  endif
342  }
343  {^ifthreed
344  call get_velocity_gradient(ixi^l,ixo^l,vr,x,3,dvrdp)
345  call get_velocity_gradient(ixi^l,ixo^l,vt,x,3,dvtdp)
346  call get_velocity_gradient(ixi^l,ixo^l,vp,x,3,dvpdp)
347  }
348 
349  ! Get total acceleration from all rays at a certain grid point
350  {do ix^db=ixomin^db,ixomax^db\}
351  ! Loop over the rays; first theta then phi radiation angle
352  ! Get weights from current ray and their position
353  do itray = 1,nthetaray
354  wyray = wy(itray)
355  y = ay(itray)
356 
357  do ipray = 1,nphiray
358  wpray = wphi(ipray)
359  phiray = aphi(ipray)
360 
361  ! Redistribute the phi rays by a small offset
362  ! if (mod(itp,3) == 1) then
363  ! phip = phip + dphi/3.0d0
364  ! elseif (mod(itp,3) == 2) then
365  ! phip = phip - dphi/3.0d0
366  ! endif
367 
368  ! === Geometrical factors ===
369  ! Make y quadrature linear in mu, not mu**2; better for gtheta,gphi
370  ! y -> mu quadrature is preserved; y=0 <=> mu=1; y=1 <=> mu=mustar
371  mustar = sqrt(max(1.0d0 - (drstar/x(ix^d,1))**2.0d0, 0.0d0))
372  costp = 1.0d0 - y*(1.0d0 - mustar)
373  costp2 = costp*costp
374  sintp = sqrt(max(1.0d0 - costp2, 0.0d0))
375  sinpp = sin(phiray)
376  cospp = cos(phiray)
377  {^nooned cott0 = cos(x(ix^d,2))/sin(x(ix^d,2))}
378 
379  ! More weight close to star, less farther away
380  wtot = wyray * wpray * (1.0d0 - mustar)
381 
382  ! Convenients a la Cranmer & Owocki (1995)
383  a1 = costp
384  a2 = sintp * cospp
385  a3 = sintp * sinpp
386 
387  ! Get total velocity gradient for one ray with given (theta', phi')
388  dvndn = a1*a1 * dvrdr(ix^d) + a2*a2 * (dvtdt(ix^d) + vrr(ix^d)) &
389  + a3*a3 * (dvpdp(ix^d) + cott0 * vtr(ix^d) + vrr(ix^d)) &
390  + a1*a2 * (dvtdr(ix^d) + dvrdt(ix^d) - vtr(ix^d)) &
391  + a1*a3 * (dvpdr(ix^d) + dvrdp(ix^d) - vpr(ix^d)) &
392  + a2*a3 * (dvpdt(ix^d) + dvtdp(ix^d) - cott0 * vpr(ix^d))
393 
394  ! No multiple resonances in CAK
395  dvndn = abs(dvndn)
396 
397  ! Convert gradient back from wind coordinates (r',theta',phi') to
398  ! stellar coordinates (r,theta,phi)
399  gcak(ix^d,1) = gcak(ix^d,1) + (dvndn/wct(ix^d,iw_rho))**cak_alpha * a1 * wtot
400  gcak(ix^d,2) = gcak(ix^d,2) + (dvndn/wct(ix^d,iw_rho))**cak_alpha * a2 * wtot
401  gcak(ix^d,3) = gcak(ix^d,3) + (dvndn/wct(ix^d,iw_rho))**cak_alpha * a3 * wtot
402  enddo
403  enddo
404  {enddo\}
405 
406  ! Normalisation for line force
407  ! NOTE: extra 1/pi factor comes from integration in radiation Phi angle
408  gcak(ixo^s,:) = (dke*gayley_qbar)**(1.0d0 - cak_alpha)/(1.0d0 - cak_alpha) &
409  * dlum/(4.0d0*dpi*drstar**2.0d0 * dclight**(1.0d0+cak_alpha)) &
410  * gcak(ixo^s,:)/dpi
411 
412  if (fix_vector_force_1d) then
413  gcak(ixo^s,2) = 0.0d0
414  gcak(ixo^s,3) = 0.0d0
415  endif
416 
417  ! Fill the nwextra slots for output
418  w(ixo^s,gcak1_) = gcak(ixo^s,1)
419  w(ixo^s,gcak2_) = gcak(ixo^s,2)
420  w(ixo^s,gcak3_) = gcak(ixo^s,3)
421 
422  end subroutine get_cak_force_vector
423 
424  !> Compute continuum radiation force from Thomson scattering
425  subroutine get_gelectron(ixI^L,ixO^L,w,x,ge)
427 
428  integer, intent(in) :: ixI^L, ixO^L
429  real(8), intent(in) :: w(ixI^S,1:nw), x(ixI^S,1:ndim)
430  real(8), intent(out):: ge(ixO^S)
431 
432  ge(ixo^s) = dke * dlum/(4.0d0*dpi * dclight * x(ixo^s,1)**2.0d0)
433 
434  end subroutine get_gelectron
435 
436  !> Check time step for total radiation contribution
437  subroutine cak_get_dt(w,ixI^L,ixO^L,dtnew,dx^D,x)
439 
440  integer, intent(in) :: ixI^L, ixO^L
441  real(8), intent(in) :: dx^D, x(ixI^S,1:ndim)
442  real(8), intent(in) :: w(ixI^S,1:nw)
443  real(8), intent(inout) :: dtnew
444 
445  ! Local variables
446  real(8) :: ge(ixO^S), max_gr, dt_cak
447 
448  call get_gelectron(ixi^l,ixo^l,w,x,ge)
449 
450  dtnew = bigdouble
451 
452  ! Get dt from line force that is saved in the w-array in nwextra slot
453  max_gr = max( maxval(abs(ge(ixo^s) + w(ixo^s,gcak1_))), epsilon(1.0d0) )
454  dt_cak = minval( sqrt(block%dx(ixo^s,1)/max_gr) )
455  dtnew = min(dtnew, courantpar*dt_cak)
456 
457  {^nooned
458  if (cak_vector_force) then
459  max_gr = max( maxval(abs(w(ixo^s,gcak2_))), epsilon(1.0d0) )
460  dt_cak = minval( sqrt(block%dx(ixo^s,1) * block%dx(ixo^s,2)/max_gr) )
461  dtnew = min(dtnew, courantpar*dt_cak)
462 
463  {^ifthreed
464  max_gr = max( maxval(abs(w(ixo^s,gcak3_))), epsilon(1.0d0) )
465  dt_cak = minval( sqrt(block%dx(ixo^s,1) * sin(block%dx(ixo^s,3))/max_gr) )
466  dtnew = min(dtnew, courantpar*dt_cak)
467  }
468  endif
469  }
470 
471  end subroutine cak_get_dt
472 
473  !> Compute velocity gradient in direction 'idir' on a non-uniform grid
474  subroutine get_velocity_gradient(ixI^L,ixO^L,vfield,x,idir,grad_vn)
476 
477  integer, intent(in) :: ixI^L, ixO^L, idir
478  real(8), intent(in) :: vfield(ixI^S), x(ixI^S,1:ndim)
479  real(8), intent(out) :: grad_vn(ixO^S)
480 
481  ! Local variables
482  real(8) :: forw(ixO^S), backw(ixO^S), cent(ixO^S)
483  integer :: jrx^L, hrx^L{^NOONED,jtx^L, htx^L}{^IFTHREED,jpx^L, hpx^L}
484 
485  ! Index +1 (j) and index -1 (h) in radial direction; kr(dir,dim)=1, dir=dim
486  jrx^l=ixo^l+kr(1,^d);
487  hrx^l=ixo^l-kr(1,^d);
488 
489  {^nooned
490  ! Index +1 (j) and index -1 (h) in polar direction
491  jtx^l=ixo^l+kr(2,^d);
492  htx^l=ixo^l-kr(2,^d);
493  }
494 
495  {^ifthreed
496  ! Index +1 (j) and index -1 (h) in azimuthal direction
497  jpx^l=ixo^l+kr(3,^d);
498  hpx^l=ixo^l-kr(3,^d);
499  }
500 
501  ! grad(v.n) on non-uniform grid according to Sundqvist & Veronis (1970)
502  select case (idir)
503  case(1) ! Radial forward, backward, and central derivatives
504  forw(ixo^s) = (x(ixo^s,1) - x(hrx^s,1)) * vfield(jrx^s) &
505  / ((x(jrx^s,1) - x(ixo^s,1)) * (x(jrx^s,1) - x(hrx^s,1)))
506 
507  backw(ixo^s) = -(x(jrx^s,1) - x(ixo^s,1)) * vfield(hrx^s) &
508  / ((x(ixo^s,1) - x(hrx^s,1)) * (x(jrx^s,1) - x(hrx^s,1)))
509 
510  cent(ixo^s) = (x(jrx^s,1) + x(hrx^s,1) - 2.0d0*x(ixo^s,1)) * vfield(ixo^s) &
511  / ((x(ixo^s,1) - x(hrx^s,1)) * (x(jrx^s,1) - x(ixo^s,1)))
512  {^nooned
513  case(2) ! Polar forward, backward, and central derivatives
514  forw(ixo^s) = (x(ixo^s,2) - x(htx^s,2)) * vfield(jtx^s) &
515  / (x(ixo^s,1) * (x(jtx^s,2) - x(ixo^s,2)) * (x(jtx^s,2) - x(htx^s,2)))
516 
517  backw(ixo^s) = -(x(jtx^s,2) - x(ixo^s,2)) * vfield(htx^s) &
518  / ( x(ixo^s,1) * (x(ixo^s,2) - x(htx^s,2)) * (x(jtx^s,2) - x(htx^s,2)))
519 
520  cent(ixo^s) = (x(jtx^s,2) + x(htx^s,2) - 2.0d0*x(ixo^s,2)) * vfield(ixo^s) &
521  / ( x(ixo^s,1) * (x(ixo^s,2) - x(htx^s,2)) * (x(jtx^s,2) - x(ixo^s,2)))
522  }
523  {^ifthreed
524  case(3) ! Azimuthal forward, backward, and central derivatives
525  forw(ixo^s) = (x(ixo^s,3) - x(hpx^s,3)) * vfield(jpx^s) &
526  / ( 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)))
527 
528  backw(ixo^s) = -(x(jpx^s,3) - x(ixo^s,3)) * vfield(hpx^s) &
529  / ( 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)))
530 
531  cent(ixo^s) = (x(jpx^s,3) + x(hpx^s,3) - 2.0d0*x(ixo^s,3)) * vfield(ixo^s) &
532  / ( 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)))
533  }
534  end select
535 
536  ! Total gradient for given velocity field
537  grad_vn(ixo^s) = backw(ixo^s) + cent(ixo^s) + forw(ixo^s)
538 
539  end subroutine get_velocity_gradient
540 
541  !> Initialise (theta',phi') radiation angles coming from stellar disc
542  subroutine rays_init(ntheta_point,nphi_point)
544 
545  ! Subroutine arguments
546  integer, intent(in) :: ntheta_point, nphi_point
547 
548  ! Local variables
549  real(8) :: ymin, ymax, phipmin, phipmax, adum
550  integer :: ii
551 
552  ! Minimum and maximum range of theta and phi rays
553  ! NOTE: theta points are cast into y-space
554  ymin = 0.0d0
555  ymax = 1.0d0
556  phipmin = -dpi !0.0d0
557  phipmax = dpi !2.0d0*dpi
558  ! dphi = (phipmax - phipmin) / nphi_point
559 
560  if (mype == 0) then
561  allocate(ay(ntheta_point))
562  allocate(wy(ntheta_point))
563  allocate(aphi(nphi_point))
564  allocate(wphi(nphi_point))
565 
566  ! theta and phi ray positions and weights: Gauss-Legendre
567  call gauss_legendre_quadrature(ymin,ymax,ntheta_point,ay,wy)
568  call gauss_legendre_quadrature(phipmin,phipmax,nphi_point,aphi,wphi)
569 
570  ! theta rays and weights: uniform
571  ! dth = 1.0d0 / nthetap
572  ! adum = ymin + 0.5d0*dth
573  ! do ip = 1,nthetap
574  ! ay(ip) = adum
575  ! wy(ip) = 1.0d0/nthetap
576  ! adum = adum + dth
577  ! !print*,'phipoints'
578  ! !print*,ip,aphi(ip),wphi(ip),dphi
579  ! enddo
580 
581  ! phi ray position and weights: uniform
582  ! adum = phipmin + 0.5d0*dphi
583  ! do ii = 1,nphi_point
584  ! aphi(ii) = adum
585  ! wphi(ii) = 1.0d0/nphi_point
586  ! adum = adum + dphi
587  ! enddo
588 
589  print*, '==========================='
590  print*, ' Radiation ray setup '
591  print*, '==========================='
592  print*, 'Theta ray points + weights '
593  do ii = 1,ntheta_point
594  print*,ii,ay(ii),wy(ii)
595  enddo
596  print*
597  print*, 'Phi ray points + weights '
598  do ii = 1,nphi_point
599  print*,ii,aphi(ii),wphi(ii)
600  enddo
601  print*
602  endif
603 
604  call mpi_barrier(icomm,ierrmpi)
605 
606  !===========================
607  ! Broadcast what mype=0 read
608  !===========================
609  if (npe > 1) then
610  call mpi_bcast(ntheta_point,1,mpi_integer,0,icomm,ierrmpi)
611  call mpi_bcast(nphi_point,1,mpi_integer,0,icomm,ierrmpi)
612 
613  if (mype /= 0) then
614  allocate(ay(ntheta_point))
615  allocate(wy(ntheta_point))
616  allocate(aphi(nphi_point))
617  allocate(wphi(nphi_point))
618  endif
619 
620  call mpi_bcast(ay,ntheta_point,mpi_double_precision,0,icomm,ierrmpi)
621  call mpi_bcast(wy,ntheta_point,mpi_double_precision,0,icomm,ierrmpi)
622  call mpi_bcast(aphi,nphi_point,mpi_double_precision,0,icomm,ierrmpi)
623  call mpi_bcast(wphi,nphi_point,mpi_double_precision,0,icomm,ierrmpi)
624  endif
625 
626  end subroutine rays_init
627 
628  !> Fast Gauss-Legendre N-point quadrature algorithm by G. Rybicki
629  subroutine gauss_legendre_quadrature(xlow,xhi,n,x,w)
630  ! Given the lower and upper limits of integration xlow and xhi, and given n,
631  ! this routine returns arrays x and w of length n, containing the abscissas
632  ! and weights of the Gauss-Legendre N-point quadrature
634 
635  ! Subroutine arguments
636  real(8), intent(in) :: xlow, xhi
637  integer, intent(in) :: n
638  real(8), intent(out) :: x(n), w(n)
639 
640  ! Local variables
641  integer :: i, j, m
642  real(8) :: p1, p2, p3, pp, xl, xm, z, z1
643  real(8), parameter :: error=3.0d-14
644 
645  m = (n + 1)/2
646  xm = 0.5d0*(xhi + xlow)
647  xl = 0.5d0*(xhi - xlow)
648 
649  do i = 1,m
650  z = cos( dpi * (i - 0.25d0)/(n + 0.5d0) )
651  z1 = 2.0d0 * z
652 
653  do while (abs(z1 - z) > error)
654  p1 = 1.0d0
655  p2 = 0.0d0
656 
657  do j = 1,n
658  p3 = p2
659  p2 = p1
660  p1 = ( (2.0d0*j - 1.0d0)*z*p2 - (j - 1.0d0)*p3 )/j
661  enddo
662 
663  pp = n*(z*p1 - p2) / (z*z - 1.0d0)
664  z1 = z
665  z = z1 - p1/pp
666  enddo
667 
668  x(i) = xm - xl*z
669  x(n+1-i) = xm + xl*z
670  w(i) = 2.0d0*xl / ((1.0d0 - z*z) * pp*pp)
671  w(n+1-i) = w(i)
672  enddo
673 
674  end subroutine gauss_legendre_quadrature
675 
676 end module mod_cak_force
subroutine mpistop(message)
Exit MPI-AMRVAC with an error message.
Definition: comm_lib.t:194
Module to include CAK radiation line force in (magneto)hydrodynamic models Computes both the force fr...
Definition: mod_cak_force.t:27
subroutine get_gelectron(ixIL, ixOL, w, x, ge)
Compute continuum radiation force from Thomson scattering.
real(8), public gayley_qbar
Definition: mod_cak_force.t:32
subroutine get_velocity_gradient(ixIL, ixOL, vfield, x, idir, grad_vn)
Compute velocity gradient in direction 'idir' on a non-uniform grid.
real(8), public gayley_q0
Definition: mod_cak_force.t:32
subroutine cak_get_dt(w, ixIL, ixOL, dtnew, dxD, x)
Check time step for total radiation contribution.
logical cak_split
To treat source term in split or unsplit (default) fashion.
Definition: mod_cak_force.t:41
subroutine cak_init(phys_gamma)
Initialize the module.
Definition: mod_cak_force.t:98
subroutine cak_params_read(files)
Public method.
Definition: mod_cak_force.t:77
subroutine gauss_legendre_quadrature(xlow, xhi, n, x, w)
Fast Gauss-Legendre N-point quadrature algorithm by G. Rybicki.
real(8), public cak_alpha
Line-ensemble parameters in the Gayley (1995) formalism.
Definition: mod_cak_force.t:32
subroutine, public set_cak_force_norm(rstar, twind)
Compute some (unitless) variables for CAK force normalisation.
subroutine get_cak_force_radial(ixIL, ixOL, wCT, w, x, gcak)
1-D CAK line force in the Gayley line-ensemble distribution parametrisation
logical fix_vector_force_1d
To activate the pure radial vector CAK line force computation.
Definition: mod_cak_force.t:50
integer gcak1_
Extra slots to store quantities in w-array.
Definition: mod_cak_force.t:68
logical cak_vector_force
To activate the vector CAK line force computation.
Definition: mod_cak_force.t:47
subroutine 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 gcak2_
Definition: mod_cak_force.t:68
integer cak_1d_opt
Switch to choose between the 1-D CAK line force options.
Definition: mod_cak_force.t:35
integer gcak3_
Definition: mod_cak_force.t:68
subroutine get_cak_force_vector(ixIL, ixOL, wCT, w, x, gcak)
Vector CAK line force in the Gayley line-ensemble distribution parametrisation.
logical cak_1d_force
To activate the original CAK 1-D line force computation.
Definition: mod_cak_force.t:44
integer nthetaray
Amount of rays in radiation polar and radiation azimuthal direction.
Definition: mod_cak_force.t:53
subroutine rays_init(ntheta_point, nphi_point)
Initialise (theta',phi') radiation angles coming from stellar disc.
integer nphiray
Definition: mod_cak_force.t:53
Module for physical and numeric constants.
Definition: mod_constants.t:2
double precision, parameter dpi
Pi.
Definition: mod_constants.t:25
double precision, parameter const_kappae
Definition: mod_constants.t:62
double precision, parameter const_c
Definition: mod_constants.t:48
double precision, parameter const_sigma
Definition: mod_constants.t:59
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
integer, dimension(3, 3) kr
Kronecker delta tensor.
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, dimension(:), allocatable, parameter d
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.
integer npe
The number of MPI tasks.
double precision unit_velocity
Physical scaling factor for velocity.
double precision unit_temperature
Physical scaling factor for temperature.
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:75
character(len=name_len) physics_type
String describing the physics type of the simulation.
Definition: mod_physics.t:19
Module with all the methods that users can customize in AMRVAC.