MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_fld.t
Go to the documentation of this file.
1!> Module for flux limited diffusion (FLD)-approximation in Radiation-(Magneto)hydrodynamics simulations
2!>
3!> Full description of RHD-FLD in
4!> Moens N., Sundqvist J.O., El Mellah I., Poniatowski L., Teunissen J. & Keppens R. 2022, A&A 657, A81
5!> Radiation-hydrodynamics with MPI-AMRVAC . Flux-limited diffusion
6!> doi:10.1051/0004-6361/202141023
7!>
8!> Full description for RMHD-FLD in
9!> N. Narechania, R. Keppens, A. ud-Doula, N. Moens & J. Sundqvist 2025, A&A 696, A131
10!> doi:10.1051/0004-6361/202452208
11!> Radiation-magnetohydrodynamics with MPI-AMRVAC using flux-limited diffusion
12
13module mod_fld
14 use mod_comm_lib, only: mpistop
15 use mod_geometry
16 implicit none
17 !> source split for energy interact and radforce:
18 logical :: fld_radforce_split = .false.
19 !> switch to handle photon tiring explicit or implicit
20 logical :: fld_tiring_explicit = .false.
21 !> Opacity value when using constant opacity
22 double precision, public :: fld_kappa0 = 0.0d0
23 !> Tolerance for bisection method for Energy sourceterms
24 !> This is a percentage of the minimum of gas- and radiation energy
25 double precision, public :: fld_bisect_tol = 1.d-4
26 !> Tolerance for radiative Energy diffusion
27 double precision, public :: fld_diff_tol = 1.d-4
28 !> handling ramp-up phase with explicit diffusion dt limit, slowly boosted
29 logical :: fld_slowsteps = .false.
30 double precision, public :: fld_boost_dt = 0.0d0
31 !> switches for local debug purposes
32 logical :: fld_force_mg_converged = .true.
34 !> switches for opacity
35 character(len=40) :: fld_opacity_law = 'const'
36 character(len=40) :: fld_opal_table = 'Y09800'
37 !> flux limiter choice
38 character(len=40) :: fld_fluxlimiter = 'Pomraning'
39 !> diffusion coefficient for multigrid method
40 integer :: i_diff_mg
41 !> diffusion coefficient stencil control
42 integer :: nth_for_diff_mg
43 !> Which method to find the root for the energy interaction polynomial
44 character(len=40) :: fld_interaction_method = 'Halley'
45 !> Abstract interface for the gas-EoS getters the radiation fluid needs
46 !> (same shape as thermal_conduction's get_var_subr).
47 abstract interface
48 subroutine fld_get_var(w,x,ixI^L,ixO^L,res)
50 integer, intent(in) :: ixI^L, ixO^L
51 double precision, intent(in) :: w(ixI^S,nw)
52 double precision, intent(in) :: x(ixI^S,1:ndim)
53 double precision, intent(out):: res(ixI^S)
54 end subroutine fld_get_var
55 end interface
56
57 !> Radiation fluid object: gas-EoS callbacks the FLD module needs, wired by
58 !> the physics module at link time (mirrors tc_fluid / rc_fluid). The
59 !> instance lives in mod_(m)hd_phys and is threaded in here as `fl`.
61 !> adiabatic index (constant for FI; a snapshot of eos%gamma)
62 double precision :: gamma
63 !> gas temperature from primitive pressure, T = p/(R*rho)
64 procedure(fld_get_var), pointer, nopass :: get_tgas => null()
65 !> R factor (mean-molecular-weight gas constant) for the emission term
66 procedure(fld_get_var), pointer, nopass :: get_rfactor => null()
67 end type fld_fluid
68
69 !> public methods
70 !> these are called in mod_hd_phys or mod_mhd_phys
71 public :: fld_fluid
72 public :: fld_init
73 public :: fld_get_radpress
75 public :: add_fld_rad_force
76 public :: fld_radforce_get_dt
77 !> wired to physics-module wrappers (which inject fld_fl) in mod_(m)hd_phys
78 public :: fld_implicit_update
79 public :: fld_evaluate_implicit
80 public :: fld_set_mg_bounds
81 ! these are made public for mod_usr purposes and diagnostics
82 public :: fld_get_radflux
83 public :: fld_get_fluxlimiter
85 public :: fld_get_opacity_prim
86 contains
87
88 !> Reading in fld-list parameters from .par file
89 subroutine fld_params_read(files)
92 character(len=*), intent(in) :: files(:)
93 integer :: n
94
95 namelist /fld_list/ fld_kappa0, fld_radforce_split, &
100
101 do n = 1, size(files)
102 open(unitpar, file=trim(files(n)), status="old")
103 read(unitpar, fld_list, end=111)
104 111 close(unitpar)
105 end do
106 end subroutine fld_params_read
107
108 !> Initialising FLD-module
109 !> Read opacities
110 !> Initialise Multigrid and adimensionalise kappa
111 subroutine fld_init()
113 use mod_variables
114 use mod_physics
117
119 fld_debug=.false.
120 fld_no_mg=.false.
121 ! initialize constant opacity with free electron Thomson scattering value
124 ! sanity checks on input
125 if(fld_kappa0<smalldouble)then
126 if(mype==0) print *,'fld_kappa0=',fld_kappa0
127 call mpistop("please set the constant opacity to a reasonable value")
128 endif
129 if(fld_bisect_tol<smalldouble)then
130 if(mype==0) print *,'fld_bisect_tol=',fld_bisect_tol
131 call mpistop("convergence tolerance for root solver too strict")
132 endif
133 if(fld_tiring_explicit)then
134 if(mype==0) print *,'Will do photon tiring explicit!!!'
135 endif
136 if(.not.fld_no_mg)then
137 if(fld_diff_tol<smalldouble)then
138 if(mype==0) print *,'fld_diff_tol=',fld_diff_tol
139 call mpistop("convergence tolerance for MG solver too strict")
140 endif
141 select case(nth_for_diff_mg)
142 case(1)
143 ! no need for stencil extension
144 case(2)
145 ! need for stencil extension
147 case default
148 call mpistop("nth_for_diff_mg must be 1 or 2")
149 end select
150 ! phys_implicit_update / phys_evaluate_implicit are wired in the physics
151 ! module (mod_(m)hd_phys) to wrappers that inject the fld_fl object;
152 ! set_mg_bounds needs no fluid object so it is bound here directly.
154 ! store the diffusion coefficient as extra variable (needed in mg vhelmholtz)
155 i_diff_mg = var_set_extravar("D", "D")
156 use_multigrid = .true.
157 ! use multigrid to solve a helmholtz equation with variable coefficient
158 ! this is stored also as extra variable in the mg solver as mg_iveps
159 mg%n_extra_vars = 1
160 mg%operator_type = mg_vhelmholtz
161 ! choice of smoother: can be mg_smoother_gs or gsrb (latter recommended)
162 mg%smoother_type = mg_smoother_gsrb
163 endif
164 !> Read in opacity table if necesary
165 if(trim(fld_opacity_law) .eq. 'opal') then
166 if(si_unit)call mpistop("adjust opal module with SI-cgs conversions for SI - or use cgs!")
168 endif
169 end subroutine fld_init
170
171 !> Set the boundaries for the diffusion of E
176
177 integer :: ib
178
179 ! Set boundary conditions for the multigrid solver
180 do ib = 1, 2*ndim
181 select case (typeboundary(iw_r_e, ib))
182 case (bc_symm)
183 ! d/dx u = 0
184 mg%bc(ib, mg_iphi)%bc_type = mg_bc_neumann
185 mg%bc(ib, mg_iphi)%bc_value = 0.0_dp
186 case (bc_asymm)
187 ! u = 0
188 mg%bc(ib, mg_iphi)%bc_type = mg_bc_dirichlet
189 mg%bc(ib, mg_iphi)%bc_value = 0.0_dp
190 case (bc_cont)
191 ! d/dx u = 0
192 mg%bc(ib, mg_iphi)%bc_type = mg_bc_neumann
193 mg%bc(ib, mg_iphi)%bc_value = 0.0_dp
194 case (bc_periodic)
195 ! Nothing to do here, this is picked up through periodB variable
196 case (bc_special)
197 if(mype==0)then
198 print *,'Special boundary for Erad needs specific user-set MG BC treatment'
199 print *,' and this could be through usr_special_mg_bc call'
200 endif
201 if (associated(usr_special_mg_bc)) then
202 call usr_special_mg_bc(ib)
203 endif
204 case default
205 call mpistop("divE_multigrid warning: unknown b.c. ")
206 end select
207 ! Neumann on diffusion coefficient is needed on all lower grid levels
208 ! d/dx u = 0
209 mg%bc(ib, mg_iveps)%bc_type = mg_bc_neumann
210 mg%bc(ib, mg_iveps)%bc_value = 0.0_dp
211 end do
212
213 end subroutine fld_set_mg_bounds
214
215
216 !> w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO
217 !> This subroutine handles the radiation force and its work added explicitly
218 !> and the energy interaction term combined with photon tiring using an implicit update
219 subroutine add_fld_rad_force(qdt,ixI^L,ixO^L,wCT,wCTprim,w,x,qsourcesplit,active,fl)
220 use mod_constants
222 use mod_geometry
223 integer, intent(in) :: ixi^l, ixo^l
224 double precision, intent(in) :: qdt, x(ixi^s,1:ndim)
225 double precision, intent(in) :: wct(ixi^s,1:nw),wctprim(ixi^s,1:nw)
226 double precision, intent(inout) :: w(ixi^s,1:nw)
227 logical, intent(in) :: qsourcesplit
228 logical, intent(inout) :: active
229 type(fld_fluid), intent(in) :: fl
230
231 integer :: idir,jdir,nth_for_fld,ix^d
232 double precision, dimension(ixI^S) :: a1,a2,a3,c0,c1,kappa
233 double precision, dimension(ixI^S) :: e_gas,e_rad,tmp
234 double precision, dimension(ixI^S,1:ndim,1:ndim) :: div_v,edd
235
236 !> Calculate and add sourceterms
237 if(qsourcesplit .eqv. fld_radforce_split) then
238 active = .true.
239 nth_for_fld=2
240 ! store here lambda in a1 and fld_R in a2
241 call fld_get_eddington(wctprim,x,ixi^l,ixo^l,edd,a1,a2,nth_for_fld,fl)
242
243 !> Photon tiring : calculate tensor grad v (named div_v here)
244 ! NOTE: This is ok for uniform Cartesian only!!!!!
245 ! TODO: introduce gradient of vector in geometry module and call that one
246 do idir = 1,ndim
247 do jdir = 1,ndim
248 call gradient(wctprim(ixi^s,iw_mom(jdir)),ixi^l,ixo^l,idir,tmp)
249 div_v(ixo^s,idir,jdir) = tmp(ixo^s)
250 enddo
251 enddo
252 ! perform contraction fe : grad(v) with fe eddington tensor
253 {^ifoned
254 a3(ixo^s) = div_v(ixo^s,1,1)*edd(ixo^s,1,1)
255 }
256 {^iftwod
257 a3(ixo^s) = div_v(ixo^s,1,1)*edd(ixo^s,1,1) &
258 + div_v(ixo^s,1,2)*edd(ixo^s,1,2) &
259 + div_v(ixo^s,2,1)*edd(ixo^s,2,1) &
260 + div_v(ixo^s,2,2)*edd(ixo^s,2,2)
261 }
262 {^ifthreed
263 a3(ixo^s) = div_v(ixo^s,1,1)*edd(ixo^s,1,1) &
264 + div_v(ixo^s,1,2)*edd(ixo^s,1,2) &
265 + div_v(ixo^s,1,3)*edd(ixo^s,1,3) &
266 + div_v(ixo^s,2,1)*edd(ixo^s,2,1) &
267 + div_v(ixo^s,2,2)*edd(ixo^s,2,2) &
268 + div_v(ixo^s,2,3)*edd(ixo^s,2,3) &
269 + div_v(ixo^s,3,1)*edd(ixo^s,3,1) &
270 + div_v(ixo^s,3,2)*edd(ixo^s,3,2) &
271 + div_v(ixo^s,3,3)*edd(ixo^s,3,3)
272 }
273
274 do idir = 1,ndim
275 call gradient(wctprim(ixi^s,iw_r_e),ixi^l,ixo^l,idir,tmp,nth_for_fld)
276 ! Radiation force = kappa*rho/c *Flux = lambda gradE
277 ! recycle grad E to store -lambda (grad E)_i
278 tmp(ixo^s) = -a1(ixo^s)*tmp(ixo^s)
279 !> Momentum equation source term
280 w(ixo^s,iw_mom(idir)) = w(ixo^s,iw_mom(idir))+ qdt*tmp(ixo^s)
281 !> Energy equation source term
282 w(ixo^s,iw_e) = w(ixo^s,iw_e) + qdt*wctprim(ixo^s,iw_mom(idir))*tmp(ixo^s)
283 enddo
284 !> photon tiring when handled explicitly
285 if(fld_tiring_explicit)then
286 w(ixo^s,iw_r_e) = w(ixo^s,iw_r_e) - qdt*wctprim(ixo^s,iw_r_e)*a3(ixo^s)
287 a3(ixo^s)=0.0d0
288 endif
289
290 call get_and_check_egas_erad_from_conserved(w,ixi^l,ixo^l,e_gas,e_rad)
291 ! BEGIN radiative exchange part with or without photon tiring included
292 call fld_get_opacity_prim(wctprim,x,ixi^l,ixo^l,kappa,fl)
293 !> Coefficients for the polynomial in Moens et al. 2022, eq 37. but with photon tiring (a3)
294 !> FI emission term: a1 carries arad*T^4 with T=(gamma-1)*e_gas/(rho*R) folded
295 !> into the e_gas^4 root-find below. gamma and R come from the EoS via fl.
296 call fl%get_Rfactor(wct,x,ixi^l,ixo^l,tmp)
297 a1(ixo^s) = qdt*kappa(ixo^s)*c_norm*arad_norm*(fl%gamma-one)**4/(wct(ixo^s,iw_rho)**3*tmp(ixo^s)**4)
298 a2(ixo^s) = c_norm*kappa(ixo^s)*wct(ixo^s,iw_rho)*qdt
299 a3(ixo^s) = a3(ixo^s)*qdt
300
301 c0(ixo^s) = ((one+a2(ixo^s)+a3(ixo^s))*e_gas(ixo^s)+a2(ixo^s)*e_rad(ixo^s))/a1(ixo^s)/(one+a3(ixo^s))
302 c1(ixo^s) = (one+a2(ixo^s)+a3(ixo^s))/a1(ixo^s)/(one+a3(ixo^s))
303
304 !> Loop over every cell for rootfinding method
305 {do ix^d = ixomin^d,ixomax^d\}
306 select case(fld_interaction_method)
307 case('Bisect')
308 call bisection_method(e_gas(ix^d),c0(ix^d),c1(ix^d))
309 case('Newton')
310 call newton_method(e_gas(ix^d),c0(ix^d),c1(ix^d))
311 case('Halley')
312 call halley_method(e_gas(ix^d),c0(ix^d),c1(ix^d))
313 case default
314 call mpistop('root-method not known')
315 end select
316 {enddo\}
317
318 e_rad(ixo^s) = (a1(ixo^s)*e_gas(ixo^s)**4.d0+e_rad(ixo^s))/(one+a2(ixo^s)+a3(ixo^s))
319
320 if(check_small_values.and..not.fix_small_values)then
321 {do ix^db= ixomin^db,ixomax^db\}
322 if(e_gas(ix^d)<small_e.or.e_rad(ix^d)<small_r_e) then
323 write(*,*) "Error in FLD add_fld_rad_force: small value"
324 write(*,*) "of internal or radiation energy density after exchange"
325 write(*,*) "Iteration: ", it, " Time: ", global_time
326 write(*,*) "Location: ", x(ix^d,:)
327 write(*,*) "Cell number: ", ix^d
328 write(*,*) "internal energy density is=",e_gas(ix^d)," versus small_e=",small_e
329 write(*,*) "radiation energy density is=",e_rad(ix^d)," versus small_r_e=",small_r_e
330 call mpistop("FLD error:May need to turn on fixes")
331 end if
332 {end do\}
333 endif
334
335 if(fix_small_values)then
336 {do ix^d = ixomin^d,ixomax^d\ }
337 e_gas(ix^d) = max(e_gas(ix^d),small_e)
338 e_rad(ix^d) = max(e_rad(ix^d),small_r_e)
339 {enddo\}
340 endif
341
342 ! END radiative exchange part with or without photon tiring included
343
344 !> Update gas-energy in w, internal + kinetic
345 w(ixo^s,iw_e) = e_gas(ixo^s)
346 w(ixo^s,iw_e) = w(ixo^s,iw_e)+half*sum(w(ixo^s,iw_mom(:))**2,dim=ndim+1)/w(ixo^s,iw_rho)
347 if(allocated(iw_mag)) then
348 w(ixo^s,iw_e) = w(ixo^s,iw_e)+half*sum(w(ixo^s,iw_mag(:))**2,dim=ndim+1)
349 endif
350 !> Update rad-energy in w
351 w(ixo^s,iw_r_e) = e_rad(ixo^s)
352 end if
353 end subroutine add_fld_rad_force
354
355 !> get dt limit for radiation force and FLD explicit source additions
356 !> NOTE: w is primitive on entry
357 subroutine fld_radforce_get_dt(w,ixI^L,ixO^L,dtnew,dx^D,x,fl)
360 use mod_geometry
362
363 integer, intent(in) :: ixi^l, ixo^l
364 double precision, intent(in) :: dx^d, x(ixi^s,1:ndim), w(ixi^s,1:nw)
365 double precision, intent(inout) :: dtnew
366 type(fld_fluid), intent(in) :: fl
367
368 integer :: idim,idims,nth_for_fld
369 double precision :: dxinv(1:ndim), max_grav
370 double precision :: lambda(ixi^s),fld_r(ixi^s)
371 double precision :: tmp(ixi^s),boostfactor
372 double precision :: max_diff,max_diff_coef,max_diff_dim,dtdifflimit
373 double precision :: cmax(ixi^s),cmaxtot(ixi^s),courantmaxtots
374
375 if(fld_debug)print *,'DT limit on entry to radforce_get_dt=',dtnew
376 nth_for_fld=2
377 call fld_get_fluxlimiter_prim(w,x,ixi^l,ixo^l,lambda,fld_r,nth_for_fld,fl)
378 if(slab_uniform) then
379 ^d&dxinv(^d)=one/dx^d;
380 do idim = 1, ndim
381 call gradient(w(ixi^s,iw_r_e),ixi^l,ixo^l,idim,tmp,nth_for_fld)
382 max_grav = maxval(dabs(-lambda(ixo^s)*tmp(ixo^s)/w(ixo^s,iw_rho)))
383 max_grav = max(max_grav, epsilon(1.0d0))
384 dtnew = min(dtnew, 1.0d0 / dsqrt(max_grav * dxinv(idim)))
385 end do
386 else
387 do idim = 1, ndim
388 call gradient(w(ixi^s,iw_r_e),ixi^l,ixo^l,idim,tmp,nth_for_fld)
389 max_grav = maxval(dabs(-lambda(ixo^s)*tmp(ixo^s)/w(ixo^s,iw_rho))/block%ds(ixo^s,idim))
390 max_grav = max(max_grav, epsilon(1.0d0))
391 dtnew = min(dtnew, 1.0d0 / dsqrt(max_grav))
392 end do
393 endif
394 if(fld_debug)print *,'DT limit after RADFORCE eff grav=',dtnew
395
396 if(fld_slowsteps) then
397 boostfactor=1.0d0
398 call fld_get_opacity_prim(w, x, ixi^l, ixo^l, tmp, fl)
399 max_diff=0.0d0
400 if(slab_uniform) then
401 max_diff_coef = maxval(c_norm*lambda(ixo^s)/(w(ixo^s,iw_rho)*tmp(ixo^s)))
402 ^d&dxinv(^d)=one/dx^d;
403 do idim = 1, ndim
404 max_diff_dim = max(2.0d0*ndim*max_diff_coef*dxinv(idim)**2, epsilon(1.0d0))
405 max_diff = max(max_diff,max_diff_dim)
406 end do
407 else
408 do idim = 1, ndim
409 max_diff_coef = maxval(c_norm*lambda(ixo^s)/(w(ixo^s,iw_rho)*tmp(ixo^s))/block%ds(ixo^s,idim)**2)
410 max_diff_dim = max(2.0d0*ndim*max_diff_coef, epsilon(1.0d0))
411 max_diff = max(max_diff,max_diff_dim)
412 end do
413 endif
414 dtdifflimit=1.0d0/max_diff
415 if(fld_debug) print *,'DT limit from diffusion is actually=',dtdifflimit
416 if(slowsteps>it-it_init+1) then
417 boostfactor=1.0d0+fld_boost_dt*(dble(it-it_init+1)/dble(slowsteps))
418 else
419 boostfactor=1.0d0+fld_boost_dt
420 endif
421 dtdifflimit=dtdifflimit*boostfactor
422 if(fld_debug) print *,'DT limit from diffusion is boosted to=',dtdifflimit
423 dtnew=min(dtnew,dtdifflimit)
424 if(fld_debug) print *,'DT limit after diffusion is =',dtnew
425 endif
426
427 ! here we interface back to fld_get_radpress
428 call phys_get_csrad2(w,x,ixi^l,ixo^l,tmp)
429 if(slab_uniform) then
430 ^d&dxinv(^d)=one/dx^d;
431 do idims=1,ndim
432 cmax(ixo^s)=dabs(w(ixo^s,iw_mom(idims)))+dsqrt(tmp(ixo^s))
433 if(idims==1) then
434 cmaxtot(ixo^s)=cmax(ixo^s)*dxinv(idims)
435 else
436 cmaxtot(ixo^s)=cmaxtot(ixo^s)+cmax(ixo^s)*dxinv(idims)
437 end if
438 end do
439 else
440 do idims=1,ndim
441 cmax(ixo^s)=dabs(w(ixo^s,iw_mom(idims)))+dsqrt(tmp(ixo^s))
442 if(idims==1) then
443 cmaxtot(ixo^s)=cmax(ixo^s)/block%ds(ixo^s,idims)
444 else
445 cmaxtot(ixo^s)=cmaxtot(ixo^s)+cmax(ixo^s)/block%ds(ixo^s,idims)
446 end if
447 end do
448 end if
449 ! courantmaxtots='max(summed c/dx)'
450 courantmaxtots=maxval(cmaxtot(ixo^s))
451 if(fld_debug)print *,'DT limit RADFORCE CSRAD=',courantpar/courantmaxtots
452 if(courantmaxtots>smalldouble) dtnew=min(dtnew,courantpar/courantmaxtots)
453 if(fld_debug)print *,'DT limit FINALLY ENFORCED IS NOW=',dtnew
454
455 end subroutine fld_radforce_get_dt
456
457 !> Sets the opacity in the w-array
458 !> by calling mod_opal_opacity
459 !> NOTE: assumes primitives in w
460 !> NOTE: assuming opacity is local, not ok with cak line force
461 subroutine fld_get_opacity_prim(w, x, ixI^L, ixO^L, fld_kappa, fl)
465 integer, intent(in) :: ixi^l, ixo^l
466 double precision, intent(in) :: w(ixi^s, 1:nw)
467 double precision, intent(in) :: x(ixi^s, 1:ndim)
468 double precision, intent(out) :: fld_kappa(ixi^s)
469 type(fld_fluid), intent(in) :: fl
470
471 integer :: ix^d
472 double precision :: rho0,temp0,kapp0
473 double precision :: temp(ixi^s)
474
475 select case (trim(fld_opacity_law))
476 case('const_norm')
477 fld_kappa(ixo^s) = fld_kappa0
478 case('const')
479 fld_kappa(ixo^s) = fld_kappa0/unit_opacity
480 case('opal')
481 call fl%get_tgas(w,x,ixi^l,ixo^l,temp)
482 {do ix^d=ixomin^d,ixomax^d\ }
483 rho0 = w(ix^d,iw_rho)*unit_density
484 temp0 = temp(ix^d)*unit_temperature
485 call set_opal_opacity(rho0,temp0,kapp0)
486 fld_kappa(ix^d) = kapp0/unit_opacity
487 {enddo\ }
488 case('special')
489 if (.not. associated(usr_special_opacity)) then
490 call mpistop("special opacity not defined")
491 endif
492 call usr_special_opacity(ixi^l, ixo^l, w, x, fld_kappa)
493 case default
494 call mpistop("Doesn't know opacity law")
495 end select
496 end subroutine fld_get_opacity_prim
497
498 !> Returns Radiation Pressure as tensor
499 !> NOTE: w is primitive on entry
500 subroutine fld_get_radpress(w, x, ixI^L, ixO^L, rad_pressure, fl)
502 integer, intent(in) :: ixi^l, ixo^l
503 double precision, intent(in) :: w(ixi^s, 1:nw)
504 double precision, intent(in) :: x(ixi^s, 1:ndim)
505 double precision, intent(out):: rad_pressure(ixi^s,1:ndim,1:ndim)
506 type(fld_fluid), intent(in) :: fl
507 integer :: i,j,nth
508 double precision :: eddington_tensor(ixi^s,1:ndim,1:ndim)
509 double precision :: lambda(ixi^s),fld_r(ixi^s)
510
511 ! always use 4th order CD here
512 nth=2
513 call fld_get_eddington(w, x, ixi^l, ixo^l, eddington_tensor, lambda, fld_r, nth, fl)
514 if(fld_debug.and..false.)then
515 print *,'In get_radPress with nth=',nth,' on ixO=',ixo^l
516 print *,'Max and Min value of fe'
517 print *,maxval(eddington_tensor(ixo^s,1:ndim,1:ndim))
518 print *,minval(eddington_tensor(ixo^s,1:ndim,1:ndim))
519 print *,'Max and Min value of Erad'
520 print *,maxval(w(ixo^s,iw_r_e))
521 print *,minval(w(ixo^s,iw_r_e))
522 print *,'End get_radPress'
523 endif
524 do i=1,ndim
525 do j=1,ndim
526 rad_pressure(ixo^s,i,j) = eddington_tensor(ixo^s,i,j)*w(ixo^s,iw_r_e)
527 enddo
528 enddo
529 end subroutine fld_get_radpress
530
531 !> This subroutine calculates flux limiter lambda according to fld_fluxlimiter
532 !> It also calculates fld_R which is ratio of radiation scaleheight and mean free path
533 !> NOTE: nth and ixI and ixO not free to choose here: TODO
534 subroutine fld_get_fluxlimiter(w,x,ixI^L,ixO^L,fld_lambda,fld_R,nth,fl)
536 use mod_geometry
539 integer, intent(in) :: ixi^l,ixo^l,nth
540 double precision, intent(in) :: w(ixi^s,1:nw)
541 double precision, intent(in) :: x(ixi^s,1:ndim)
542 double precision, intent(out) :: fld_r(ixi^s),fld_lambda(ixi^s)
543 type(fld_fluid), intent(in) :: fl
544
545 double precision :: wprim(ixi^s,1:nw)
546
547 wprim(ixi^s,1:nw)=w(ixi^s,1:nw)
548 call phys_to_primitive(ixi^l,ixi^l,wprim,x)
549 call fld_get_fluxlimiter_prim(wprim,x,ixi^l,ixo^l,fld_lambda,fld_r,nth,fl)
550
551 end subroutine fld_get_fluxlimiter
552
553 !> This subroutine calculates flux limiter lambda according to fld_fluxlimiter
554 !> It also calculates fld_R which is ratio of radiation scaleheight and mean free path
555 !> NOTE: this one operates on primitives
556 !> NOTE: nth and ixI and ixO not free to choose
557 subroutine fld_get_fluxlimiter_prim(w,x,ixI^L,ixO^L,fld_lambda,fld_R,nth,fl)
559 use mod_geometry
561 integer, intent(in) :: ixi^l,ixo^l,nth
562 double precision, intent(in) :: w(ixi^s,1:nw)
563 double precision, intent(in) :: x(ixi^s,1:ndim)
564 double precision, intent(out) :: fld_r(ixi^s),fld_lambda(ixi^s)
565 type(fld_fluid), intent(in) :: fl
566
567 integer :: idir, ix^d
568 double precision :: kappa(ixi^s),normgrad2(ixi^s)
569 double precision :: grad_r_e(ixi^s)
570
571 select case(fld_fluxlimiter)
572 case('Diffusion')
573 ! optically thick limit
574 fld_lambda(ixo^s) = 1.d0/3.d0
575 fld_r(ixo^s) = zero
576 case('FreeStream')
577 ! optically thin limit
578 normgrad2(ixo^s) = zero
579 do idir=1,ndim
580 call gradient(w(ixi^s,iw_r_e),ixi^l,ixo^l,idir,grad_r_e,nth)
581 normgrad2(ixo^s) = normgrad2(ixo^s)+grad_r_e(ixo^s)**2
582 end do
583 call fld_get_opacity_prim(w,x,ixi^l,ixo^l,kappa,fl)
584 ! Calculate R everywhere
585 ! |grad E|/(rho kappa E)
586 fld_r(ixo^s) = dsqrt(normgrad2(ixo^s))/(kappa(ixo^s)*w(ixo^s,iw_rho)*w(ixo^s,iw_r_e))
587 where(normgrad2(ixo^s)<smalldouble**2)
588 ! treat uniform case as diffusion limit
589 fld_r(ixo^s)=zero
590 fld_lambda(ixo^s) = 1.0d0/3.0d0
591 elsewhere
592 fld_lambda(ixo^s) = one/fld_r(ixo^s)
593 endwhere
594 case('Pomraning')
595 ! Calculate R everywhere
596 ! |grad E|/(rho kappa E)
597 normgrad2(ixo^s) = zero
598 do idir = 1,ndim
599 call gradient(w(ixi^s,iw_r_e),ixi^l,ixo^l,idir,grad_r_e,nth)
600 normgrad2(ixo^s) = normgrad2(ixo^s) + grad_r_e(ixo^s)**2
601 end do
602 call fld_get_opacity_prim(w,x,ixi^l,ixo^l,kappa,fl)
603 fld_r(ixo^s) = dsqrt(normgrad2(ixo^s))/(kappa(ixo^s)*w(ixo^s,iw_rho)*w(ixo^s,iw_r_e))
604 ! Calculate the flux limiter, lambda
605 ! Levermore and Pomraning: lambda = (2 + R)/(6 + 3R + R^2)
606 fld_lambda(ixo^s) = (2.d0+fld_r(ixo^s))/(6.d0+3*fld_r(ixo^s)+fld_r(ixo^s)**2)
607 case('Minerbo')
608 ! Calculate R everywhere
609 ! |grad E|/(rho kappa E)
610 normgrad2(ixo^s) = zero
611 do idir = 1,ndim
612 call gradient(w(ixi^s,iw_r_e),ixi^l,ixo^l,idir,grad_r_e,nth)
613 normgrad2(ixo^s) = normgrad2(ixo^s) + grad_r_e(ixo^s)**2
614 end do
615 call fld_get_opacity_prim(w, x, ixi^l, ixo^l, kappa, fl)
616 fld_r(ixo^s) = dsqrt(normgrad2(ixo^s))/(kappa(ixo^s)*w(ixo^s,iw_rho)*w(ixo^s,iw_r_e))
617 ! Calculate the flux limiter, lambda
618 ! Minerbo:
619 {do ix^d = ixomin^d,ixomax^d\ }
620 if(fld_r(ix^d) .lt. 3.d0/2.d0) then
621 fld_lambda(ix^d) = 2.d0/(3.d0+dsqrt(9.d0+12.d0*fld_r(ix^d)**2))
622 else
623 fld_lambda(ix^d) = 1.d0/(1.d0+fld_r(ix^d)+dsqrt(1.d0+2.d0*fld_r(ix^d)))
624 endif
625 {enddo\}
626 case('special')
627 if (.not. associated(usr_special_fluxlimiter)) then
628 call mpistop("special fluxlimiter not defined")
629 endif
630 call usr_special_fluxlimiter(ixi^l,ixo^l,w,x,fld_lambda,fld_r)
631 case default
632 call mpistop('Fluxlimiter unknown')
633 end select
634
635 end subroutine fld_get_fluxlimiter_prim
636
637 !> Calculate Radiation Flux
638 !> NOTE: only for diagnostics purposes (w conservative on entry)
639 !> This returns cell centered values for radiation flux
640 subroutine fld_get_radflux(w, x, ixI^L, ixO^L, rad_flux, fl)
642 use mod_geometry
644 integer, intent(in) :: ixi^l, ixo^l
645 double precision, intent(in) :: w(ixi^s, 1:nw)
646 double precision, intent(in) :: x(ixi^s, 1:ndim)
647 double precision, intent(out) :: rad_flux(ixi^s, 1:ndim)
648 type(fld_fluid), intent(in) :: fl
649
650 integer :: idir,nth_for_fld
651 double precision :: wprim(ixi^s,1:nw)
652 double precision :: grad_r_e(ixi^s)
653 double precision :: kappa(ixi^s), lambda(ixi^s), fld_r(ixi^s)
654
655 wprim(ixi^s,1:nw)=w(ixi^s,1:nw)
656 call phys_to_primitive(ixi^l,ixi^l,wprim,x)
657
658 call fld_get_opacity_prim(wprim, x, ixi^l, ixo^l, kappa, fl)
659 ! always use 4th order CD here
660 nth_for_fld=2
661 call fld_get_fluxlimiter_prim(wprim, x, ixi^l, ixo^l, lambda, fld_r, nth_for_fld, fl)
662 !> Calculate the Flux using the fld closure relation
663 !> F = -c*lambda/(kappa*rho) *grad E
664 do idir = 1,ndim
665 call gradient(wprim(ixi^s,iw_r_e),ixi^l,ixo^l,idir,grad_r_e,nth_for_fld)
666 rad_flux(ixo^s,idir)=-(c_norm*lambda(ixo^s)/(kappa(ixo^s)*wprim(ixo^s,iw_rho)))*grad_r_e(ixo^s)
667 end do
668 end subroutine fld_get_radflux
669
670 !> Calculate Eddington-tensor (where w is primitive)
671 !> also feeds back the flux limiter lambda and R
672 subroutine fld_get_eddington(w, x, ixI^L, ixO^L, eddington_tensor, lambda, fld_R, nth, fl)
674 use mod_geometry
675 integer, intent(in) :: ixI^L, ixO^L, nth
676 double precision, intent(in) :: w(ixI^S, 1:nw)
677 double precision, intent(in) :: x(ixI^S, 1:ndim)
678 double precision, intent(out) :: eddington_tensor(ixI^S,1:ndim,1:ndim)
679 double precision, intent(out) :: lambda(ixI^S),fld_R(ixI^S)
680 type(fld_fluid), intent(in) :: fl
681
682 integer :: idir,jdir
683 double precision :: normgrad2(ixI^S)
684 double precision :: tmp(ixI^S),grad_r_e(ixI^S,1:ndim)
685 double precision :: nn_regularized(ixI^S,1:ndim,1:ndim)
686
687 normgrad2(ixo^s) = zero
688 do idir = 1,ndim
689 call gradient(w(ixi^s, iw_r_e),ixi^l,ixo^l,idir,tmp,nth)
690 grad_r_e(ixo^s,idir)=tmp(ixo^s)
691 normgrad2(ixo^s)=normgrad2(ixo^s)+tmp(ixo^s)**2
692 end do
693 do idir = 1,ndim
694 do jdir = 1,ndim
695 if(idir==jdir)then
696 nn_regularized(ixo^s,idir,jdir)=(grad_r_e(ixo^s,idir)*grad_r_e(ixo^s,jdir)+smalldouble**2)/(normgrad2(ixo^s)+smalldouble**2)
697 else
698 nn_regularized(ixo^s,idir,jdir)=(grad_r_e(ixo^s,idir)*grad_r_e(ixo^s,jdir))/(normgrad2(ixo^s)+smalldouble**2)
699 endif
700 enddo
701 enddo
702 ! get lambda and R
703 call fld_get_fluxlimiter_prim(w,x,ixi^l,ixo^l,lambda,fld_r,nth,fl)
704 ! store f_e= lambda + lambda^2 R^2
705 tmp(ixo^s) = lambda(ixo^s)+(lambda(ixo^s)*fld_r(ixo^s))**2
706 do idir = 1,ndim
707 ! first compute the isotropic (diagonal) part
708 eddington_tensor(ixo^s,idir,idir) = half*(one-tmp(ixo^s))
709 enddo
710 do idir = 1,ndim
711 do jdir = 1,ndim
712 ! initialize off-diagonal part here
713 if(idir .ne. jdir) eddington_tensor(ixo^s,idir,jdir) = zero
714 ! add part depending on unit vectors along gradient E
715 eddington_tensor(ixo^s,idir,jdir) = eddington_tensor(ixo^s,idir,jdir)+&
716 half*(3.d0*tmp(ixo^s)-one)*nn_regularized(ixo^s,idir,jdir)
717 enddo
718 enddo
719 end subroutine fld_get_eddington
720
721 subroutine get_and_check_egas_erad_from_conserved(w,ixI^L,ixO^L,e_gas,E_rad)
723 integer, intent(in) :: ixI^L, ixO^L
724 double precision, intent(in) :: w(ixI^S, 1:nw)
725 double precision, intent(out) :: e_gas(ixI^S),E_rad(ixI^S)
726 integer :: ix^D
727
728 !> e_gas is the INTERNAL ENERGY without KINETIC ENERGY
729 e_gas(ixo^s) = w(ixo^s,iw_e)-half*sum(w(ixo^s,iw_mom(:))**2,dim=ndim+1)/w(ixo^s,iw_rho)
730 if(allocated(iw_mag)) then
731 e_gas(ixo^s) = e_gas(ixo^s)-half*sum(w(ixo^s,iw_mag(:))**2,dim=ndim+1)
732 endif
733 e_rad(ixo^s) = w(ixo^s,iw_r_e)
734
735 if(check_small_values.and..not.fix_small_values)then
736 {do ix^db= ixomin^db,ixomax^db\}
737 if(e_gas(ix^d)<small_e.or.e_rad(ix^d)<small_r_e) then
738 write(*,*) "Error in FLD get_egas_Erad: small value"
739 write(*,*) "Iteration: ", it, " Time: ", global_time
740 write(*,*) "Cell number: ", ix^d
741 write(*,*) "internal energy density is=",e_gas(ix^d)," versus small_e=",small_e
742 write(*,*) "radiation energy density is=",e_rad(ix^d)," versus small_r_e=",small_r_e
743 call mpistop("FLD error:May need to turn on fixes")
744 end if
745 {end do\}
746 endif
747
748 if(fix_small_values)then
749 {do ix^d = ixomin^d,ixomax^d\ }
750 e_gas(ix^d) = max(e_gas(ix^d),small_e)
751 e_rad(ix^d) = max(e_rad(ix^d),small_r_e)
752 {enddo\}
753 endif
754
756
757 !> Calling all subroutines to perform the multigrid method
758 !> Communicates rad_e and diff_coeff to multigrid library
759 !> Advance psa=psb+dtfactor*qdt*F_im(psa)
760 subroutine fld_implicit_update(dtfactor,qdt,qtC,psa,psb,fl)
762 use mod_forest
765
766 type(state), target :: psa(max_blocks)
767 type(state), target :: psb(max_blocks)
768 double precision, intent(in) :: qdt
769 double precision, intent(in) :: qtc
770 double precision, intent(in) :: dtfactor
771 type(fld_fluid), intent(in) :: fl
772
773 integer, parameter :: max_its = 100
774 integer :: n,ixo^l,ix^d
775 double precision :: res, max_residual, mg_lambda, fac
776 double precision :: wmax(nw),wmin(nw)
777 double precision :: wmaxb(nw),wminb(nw)
778 integer :: iigrid, igrid
779
780 ! we need first to compute the (variable) diffusion coefficient on entire grid
781 ! this must be done in mesh+1 ghostcell layer
782 call update_diffcoeff(psa,fl)
783
784 ! now we multiply the diffusion coefficient with dtfactor*dt on entire mesh+1 domain
785 ixo^l=ixm^ll^ladd1;
786 do iigrid=1,igridstail; igrid=igrids(iigrid);
787 {do ix^d = ixomin^d,ixomax^d\ }
788 psa(igrid)%w(ix^d,i_diff_mg)= psa(igrid)%w(ix^d,i_diff_mg)*dtfactor*qdt
789 {enddo\}
790 end do
791
792 fac = 1.0d0
793 max_residual = fld_diff_tol
794
795 if(fld_debug)then
796 call get_global_maxima(wmax,psa)
797 call get_global_minima(wmin,psa)
798 call get_global_maxima(wmaxb,psb)
799 call get_global_minima(wminb,psb)
800 if(mype==0)then
801 ! the MG needs to be scaled such that everything is order unity
802 print *,'Currently at time=',global_time,' time step=',qdt,' dtfactor=',dtfactor
803 print *,'at start of MG solver, we have fld_diff_tol =',fld_diff_tol
804 print *,'at start of MG solver, we have LHS E_rad range as :',wmax(iw_r_e),wmin(iw_r_e)
805 print *,'at start of MG solver, we have Diff coeff range as:',wmax(i_diff_mg),wmin(i_diff_mg)
806 print *,'at start of MG solver, we have density range as :',wmax(iw_rho),wmin(iw_rho)
807 print *,'at start of MG solver, we have RHS E_rad range as :',wmaxb(iw_r_e),wminb(iw_r_e)
808 print *,'at start of MG solver, we have qdt as',qdt,' and max_residual=',max_residual
809 print *,'at start of MG solver, ratio coeffs on level 1 =',wmax(i_diff_mg)/(dx(1,1)**2)
810 print *,'at start of MG solver, ratio coeffs on level max=',wmax(i_diff_mg)/(dx(1,refine_max_level)**2)
811 endif
812 endif
813
814 call mg_set_methods(mg)
815 if(.not. mg%is_allocated) call mpistop("multigrid tree not allocated yet")
816
817 ! Here we handle the global helmholtz problem with variable coefficient
818 ! The equation we solve is div([D]^n nabla Erad^(n+1)) -(1/dt)Erad^(n+1)=-(1/dt)Erad^n
819 ! we reformulate to div([D x dt]^n nabla Erad^(n+1)) -Erad^(n+1)=-Erad^n
820 ! Helmholtz equation is div(eps nabla phi) -mg_lambda phi = f
821 ! hence eps is our variable coefficient dt x D=fld_lambda*c/(kappa*rho)
822 ! hence phi is Erad and mg_lambda is unity
823 call vhelmholtz_set_lambda(fac)
824 ! copy in the (variable) diffusion coefficient in mg_iveps
825 ! NOTE: this copies also the ghostcell values for this coefficient to mg
826 call mg_copy_to_tree(i_diff_mg, mg_iveps, factor=fac, state_from=psa)
827 ! copy in the Erad variable in mg_iphi (the one we solve for)
828 call mg_copy_to_tree(iw_r_e, mg_iphi, factor=fac, state_from=psa)
829 ! copy in RHS f factor as Erad with factor -1
830 call mg_copy_to_tree(iw_r_e, mg_irhs, factor=-fac, state_from=psb)
831 ! becuase the variable coefficient is needed on lower grid levels in mg
832 ! we need to restrict and adopt BCs for this variable: Neumann is set
833 call mg_restrict(mg, mg_iveps)
834 call mg_fill_ghost_cells(mg, mg_iveps)
835 ! Now try solving with MG
836 call mg_fas_fmg(mg, .true., max_res=res)
837 if(fld_debug.and.mype==0)print *,'MG residual obtained with FMG is =',res
838 do n = 1, max_its
839 if(res < max_residual) exit
840 call mg_fas_vcycle(mg, max_res=res)
841 if(fld_debug.and.mype==0)print *,'MG residual obtained with Vcycle =',res,' at step =',n
842 end do
843 if(fld_debug.and.mype==0)print *,'FINAL MG residual obtained is =',res
844 if(res .ge. max_residual) then
845 if (mype == 0) then
846 write(*,*) it, ' residual from MG ', res
847 write(*,*) it, ' max_residual in MG ', max_residual
848 write(*,*) it, ' dtfactor*qdt in MG ', qdt*dtfactor
849 print *,'Currently at time=',global_time,' time step=',qdt,' dtfactor=',dtfactor
850 endif
852 call mpistop("no convergence in MG")
853 else
854 if(mype==0) write(*,*) 'WARNING for it=',it,' NO CONVERGENCE IN MG but still carry on'
855 endif
856 end if
857 ! copy back the Erad variable in iw_r_e
858 call mg_copy_from_tree_gc(mg_iphi, iw_r_e, state_to=psa)
859
860 if(check_small_values.and..not.fix_small_values)then
861 ixo^l=ixm^ll;
862 do iigrid=1,igridstail; igrid=igrids(iigrid);
863 {do ix^db= ixomin^db,ixomax^db\}
864 if(psa(igrid)%w(ix^d,iw_r_e)<small_r_e) then
865 write(*,*) "Error in FLD fld_implicit_update: small value"
866 write(*,*) "of radiation energy density after MG"
867 write(*,*) "Iteration: ", it, " Time: ", global_time
868 write(*,*) "Location: ", psa(igrid)%x(ix^d,:)," on grid",igrid
869 write(*,*) "Cell number: ", ix^d
870 write(*,*) "radiation energy density is=",psa(igrid)%w(ix^d,iw_r_e)," versus small_r_e=",small_r_e
871 call mpistop("FLD error:May need to turn on fixes")
872 end if
873 {end do\}
874 end do
875 endif
876
877 if(fix_small_values)then
878 ixo^l=ixm^ll;
879 do iigrid=1,igridstail; igrid=igrids(iigrid);
880 {do ix^d = ixomin^d,ixomax^d\ }
881 psa(igrid)%w(ix^d,iw_r_e)= max(psa(igrid)%w(ix^d,iw_r_e),small_r_e)
882 {enddo\}
883 end do
884 endif
885
886 end subroutine fld_implicit_update
887
888 subroutine update_diffcoeff(psa,fl)
890 type(state), target :: psa(max_blocks)
891 type(fld_fluid), intent(in) :: fl
892
893 integer :: iigrid, igrid, ixO^L
894
895 ! we will need diffusion coefficients in i-1 i i+1
896 ixo^l=ixm^ll^ladd1;
897 !$OMP PARALLEL DO PRIVATE(igrid)
898 do iigrid=1,igridstail; igrid=igrids(iigrid);
899 call fld_get_diffcoef_central(psa(igrid)%w, psa(igrid)%x, ixg^ll, ixo^l, fl)
900 end do
901 !$OMP END PARALLEL DO
902
903 end subroutine update_diffcoeff
904
905 !> inplace update of psa==>F_im(psa)
906 subroutine fld_evaluate_implicit(qtC,psa,fl)
908 type(state), target :: psa(max_blocks)
909 double precision, intent(in) :: qtc
910 type(fld_fluid), intent(in) :: fl
911 integer :: iigrid, igrid
912 integer :: ixo^l
913
914 call update_diffcoeff(psa,fl)
915
916 ixo^l=ixm^ll;
917 !$OMP PARALLEL DO PRIVATE(igrid)
918 do iigrid=1,igridstail; igrid=igrids(iigrid);
919 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
920 call evaluate_diffterm_onegrid(ixg^ll,ixo^l,psa(igrid)%w,psa(igrid)%x)
921 end do
922 !$OMP END PARALLEL DO
923
924 end subroutine fld_evaluate_implicit
925
926 !> inplace update of psa==>F_im(psa)
927 subroutine evaluate_diffterm_onegrid(ixI^L,ixO^L,w,x)
929 integer, intent(in) :: ixI^L, ixO^L
930 double precision, intent(inout) :: w(ixI^S, 1:nw)
931 double precision, intent(in) :: x(ixI^S, 1:ndim)
932
933 double precision :: divF(ixI^S)
934 integer :: idir, jxO^L, hxO^L
935
936 if(.not.slab)call mpistop("laplacian coded up for uniform cartesian grid")
937
938 ! Here we use diffusion coefficients in positions i-1 i i+1 and exploit harmonic means i.e. 2ab/(a+b)
939 ! since this is how the multigrid library handles the div(eps nabla phi) term in Helmholtz equation
940 ! div(eps nabla phi) - mg_lambda phi = f
941 divf(ixo^s) = 0.d0
942 do idir = 1,ndim
943 hxo^l=ixo^l-kr(idir,^d);
944 jxo^l=ixo^l+kr(idir,^d);
945 divf(ixo^s) = divf(ixo^s) + &
946 (w(jxo^s,iw_r_e)*two*w(ixo^s,i_diff_mg)*w(jxo^s,i_diff_mg)/(w(ixo^s,i_diff_mg) + w(jxo^s,i_diff_mg)) &
947 -w(ixo^s,iw_r_e)*(two*w(ixo^s,i_diff_mg)*w(jxo^s,i_diff_mg)/(w(ixo^s,i_diff_mg) + w(jxo^s,i_diff_mg)) &
948 +two*w(ixo^s,i_diff_mg)*w(hxo^s,i_diff_mg)/(w(ixo^s,i_diff_mg) + w(hxo^s,i_diff_mg))) &
949 +w(hxo^s,iw_r_e)*two*w(ixo^s,i_diff_mg)*w(hxo^s,i_diff_mg)/(w(ixo^s,i_diff_mg) + w(hxo^s,i_diff_mg)))/dxlevel(idir)**2
950 ! below uses artihmetic mean, different from mg method
951 !divF(ixO^S) = divF(ixO^S) + &
952 ! (w(jxO^S,iw_r_e)*half*(w(ixO^S,i_diff_mg) + w(jxO^S,i_diff_mg)) &
953 ! -w(ixO^S,iw_r_e)*(half*(w(ixO^S,i_diff_mg) + w(jxO^S,i_diff_mg)) &
954 ! +half*(w(ixO^S,i_diff_mg) + w(hxO^S,i_diff_mg))) &
955 ! +w(hxO^S,iw_r_e)*half*(w(ixO^S,i_diff_mg) + w(hxO^S,i_diff_mg)))/dxlevel(idir)**2
956 enddo
957 ! only the E variable is handled implicitly, all else must be zero here
958 w(ixo^s,1:nw)=zero
959 w(ixo^s,iw_r_e) = divf(ixo^s)
960
961 end subroutine evaluate_diffterm_onegrid
962
963 !> Calculates cell-centered diffusion coefficient to be used in multigrid
964 subroutine fld_get_diffcoef_central(w, x, ixI^L, ixO^L, fl)
966 use mod_geometry
969 integer, intent(in) :: ixi^l, ixo^l
970 double precision, intent(in) :: x(ixi^s,1:ndim)
971 double precision, intent(inout) :: w(ixi^s,1:nw)
972 type(fld_fluid), intent(in) :: fl
973
974 double precision :: wprim(ixi^s,1:nw)
975 double precision :: kappa(ixi^s),lambda(ixi^s),fld_r(ixi^s)
976
977 wprim(ixi^s,1:nw)=w(ixi^s,1:nw)
978 ! ensure entries in entire ixI range
979 call phys_to_primitive(ixi^l,ixi^l,wprim,x)
980 call fld_get_opacity_prim(wprim, x, ixi^l, ixo^l, kappa, fl)
981 ! note that we use central difference here (last argument is 1 or 2)
982 call fld_get_fluxlimiter_prim(wprim, x, ixi^l, ixo^l, lambda, fld_r, nth_for_diff_mg, fl)
983 w(ixo^s,i_diff_mg) = c_norm*lambda(ixo^s)/(kappa(ixo^s)*wprim(ixo^s,iw_rho))
984 if(associated(usr_special_diffcoef)) call usr_special_diffcoef(w, wprim, x, ixi^l, ixo^l)
985 if(minval(w(ixo^s,i_diff_mg))<smalldouble) then
986 print *,'min diffcoef=',minval(w(ixo^s,i_diff_mg))
987 call mpistop("too small diffusion coefficient")
988 endif
989 if(maxval(w(ixo^s,i_diff_mg))>bigdouble) call mpistop("too large diffusion coefficient")
990
991 if(fld_debug.and..false.)then
992 print *,'setting diffcoefs with data on',ixi^l
993 print *,'min diffcoef=',minval(w(ixo^s,i_diff_mg))
994 print *,'min lambda kappa rho fld_R'
995 print *,minval(lambda(ixo^s))
996 print *,minval(kappa(ixo^s))
997 print *,minval(wprim(ixo^s,iw_rho))
998 print *,minval(fld_r(ixo^s))
999 print *,'max diffcoef=',maxval(w(ixo^s,i_diff_mg))
1000 print *,'max lambda kappa rho fld_R'
1001 print *,maxval(lambda(ixo^s))
1002 print *,maxval(kappa(ixo^s))
1003 print *,maxval(wprim(ixo^s,iw_rho))
1004 print *,maxval(fld_r(ixo^s))
1005 print *,'done setting diffcoefs in slot',i_diff_mg,' on range',ixo^l
1006 endif
1007
1008 end subroutine fld_get_diffcoef_central
1009
1010
1011 !> Find the root of the 4th degree polynomial using the bisection method
1012 subroutine bisection_method(e_gas, c0, c1)
1014 double precision, intent(in) :: c0, c1
1015 double precision, intent(inout) :: e_gas
1016 double precision :: bisect_a, bisect_b, bisect_c
1017 integer :: n, max_its
1018
1019 n = 0
1020 max_its = 100
1021 bisect_a = zero
1022 bisect_b = min(dabs(c0/c1),dabs(c0)**(1.d0/4.d0))+smalldouble
1023 do while(dabs(bisect_b-bisect_a) .ge. fld_bisect_tol)
1024 bisect_c = (bisect_a + bisect_b)/two
1025 n = n +1
1026 if(n .gt. max_its) then
1027 call mpistop('No convergece in bisection scheme')
1028 endif
1029 if(polynomial_bisection(bisect_a, c0, c1)*&
1030 polynomial_bisection(bisect_b, c0, c1) .lt. zero) then
1031 if(polynomial_bisection(bisect_a, c0, c1)*&
1032 polynomial_bisection(bisect_c, c0, c1) .lt. zero) then
1033 bisect_b = bisect_c
1034 elseif(polynomial_bisection(bisect_b, c0, c1)*&
1035 polynomial_bisection(bisect_c, c0, c1) .lt. zero) then
1036 bisect_a = bisect_c
1037 elseif(polynomial_bisection(bisect_a, c0, c1) .eq. zero) then
1038 bisect_b = bisect_a
1039 bisect_c = bisect_a
1040 goto 2435
1041 elseif(polynomial_bisection(bisect_b, c0, c1) .eq. zero) then
1042 bisect_a = bisect_b
1043 bisect_c = bisect_b
1044 goto 2435
1045 elseif(polynomial_bisection(bisect_c, c0, c1) .eq. zero) then
1046 bisect_a = bisect_c
1047 bisect_b = bisect_c
1048 goto 2435
1049 else
1050 call mpistop("Problem with fld bisection method")
1051 endif
1052 elseif(polynomial_bisection(bisect_a, c0, c1) &
1053 - polynomial_bisection(bisect_b, c0, c1) .lt. fld_bisect_tol*polynomial_bisection(bisect_a, c0, c1)) then
1054 goto 2435
1055 else
1056 bisect_a = e_gas
1057 bisect_b = e_gas
1058 if(fld_debug)print*, "IGNORING GAS-RAD ENERGY EXCHANGE ", c0, c1
1059 if(fld_debug)print*, polynomial_bisection(bisect_a, c0, c1), polynomial_bisection(bisect_b, c0, c1)
1060 call mpistop('issues in bisection scheme')
1061 if(polynomial_bisection(bisect_a, c0, c1) .le. smalldouble) then
1062 bisect_b = bisect_a
1063 elseif(polynomial_bisection(bisect_a, c0, c1) .le. smalldouble) then
1064 bisect_a = bisect_b
1065 endif
1066 goto 2435
1067 endif
1068 enddo
1069 2435 e_gas = (bisect_a + bisect_b)/two
1070 end subroutine bisection_method
1071
1072 !> Find the root of the 4th degree polynomial using the Newton method
1073 subroutine newton_method(e_gas, c0, c1)
1075 double precision, intent(in) :: c0, c1
1076 double precision, intent(inout) :: e_gas
1077 double precision :: xval, yval, der, deltax
1078 integer :: ii
1079
1080 yval = bigdouble
1081 xval = e_gas
1082 der = one
1083 deltax = one
1084 ii = 0
1085 !> Compare error with dx = dx/dy dy
1086 do while(dabs(deltax) .gt. fld_bisect_tol)
1087 yval = polynomial_bisection(xval, c0, c1)
1088 der = dpolynomial_bisection(xval, c0, c1)
1089 deltax = -yval/der
1090 xval = xval + deltax
1091 ii = ii + 1
1092 if(ii .gt. 1d3) then
1093 if(fld_debug)print*, 'skip to bisection algorithm'
1094 call bisection_method(e_gas, c0, c1)
1095 return
1096 endif
1097 enddo
1098 e_gas = xval
1099 end subroutine newton_method
1100
1101 !> Find the root of the 4th degree polynomial using the Halley method
1102 subroutine halley_method(e_gas, c0, c1)
1104 double precision, intent(in) :: c0, c1
1105 double precision, intent(inout) :: e_gas
1106 double precision :: xval, yval, der, dder, deltax
1107 integer :: ii
1108
1109 yval = bigdouble
1110 xval = e_gas
1111 der = one
1112 dder = one
1113 deltax = one
1114 ii = 0
1115 !> Compare error with dx = dx/dy dy
1116 do while (dabs(deltax) .gt. fld_bisect_tol)
1117 yval = polynomial_bisection(xval, c0, c1)
1118 der = dpolynomial_bisection(xval, c0, c1)
1119 dder = ddpolynomial_bisection(xval, c0, c1)
1120 deltax = -two*yval*der/(two*der**2 - yval*dder)
1121 xval = xval + deltax
1122 ii = ii + 1
1123 if(ii .gt. 1d3) then
1124 if(fld_debug)print*, 'skip to Newton algorithm'
1125 call newton_method(e_gas, c0, c1)
1126 return
1127 endif
1128 enddo
1129 e_gas = xval
1130 end subroutine halley_method
1131
1132 !> Evaluate polynomial at argument e_gas
1133 function polynomial_bisection(e_gas, c0, c1) result(val)
1135 double precision, intent(in) :: e_gas
1136 double precision, intent(in) :: c0, c1
1137 double precision :: val
1138
1139 val = e_gas**4.d0 + c1*e_gas - c0
1140 end function polynomial_bisection
1141
1142 !> Evaluate first derivative of polynomial at argument e_gas
1143 function dpolynomial_bisection(e_gas, c0, c1) result(der)
1145 double precision, intent(in) :: e_gas
1146 double precision, intent(in) :: c0, c1
1147 double precision :: der
1148
1149 der = 4.d0*e_gas**3.d0 + c1
1150 end function dpolynomial_bisection
1151
1152 !> Evaluate second derivative of polynomial at argument e_gas
1153 function ddpolynomial_bisection(e_gas, c0, c1) result(dder)
1155 double precision, intent(in) :: e_gas
1156 double precision, intent(in) :: c0, c1
1157 double precision :: dder
1158
1159 dder = 4.d0*3.d0*e_gas**2.d0
1160 end function ddpolynomial_bisection
1161end module mod_fld
Abstract interface for the gas-EoS getters the radiation fluid needs (same shape as thermal_conductio...
Definition mod_fld.t:48
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Module for physical and numeric constants.
double precision, parameter one
Module for flux limited diffusion (FLD)-approximation in Radiation-(Magneto)hydrodynamics simulations...
Definition mod_fld.t:13
subroutine fld_get_eddington(w, x, ixil, ixol, eddington_tensor, lambda, fld_r, nth, fl)
Calculate Eddington-tensor (where w is primitive) also feeds back the flux limiter lambda and R.
Definition mod_fld.t:673
logical fld_no_mg
Definition mod_fld.t:33
double precision, public fld_bisect_tol
Tolerance for bisection method for Energy sourceterms This is a percentage of the minimum of gas- and...
Definition mod_fld.t:25
subroutine, public fld_radforce_get_dt(w, ixil, ixol, dtnew, dxd, x, fl)
get dt limit for radiation force and FLD explicit source additions NOTE: w is primitive on entry
Definition mod_fld.t:358
logical fld_force_mg_converged
switches for local debug purposes
Definition mod_fld.t:32
double precision, public fld_diff_tol
Tolerance for radiative Energy diffusion.
Definition mod_fld.t:27
subroutine update_diffcoeff(psa, fl)
Definition mod_fld.t:889
subroutine fld_params_read(files)
public methods these are called in mod_hd_phys or mod_mhd_phys
Definition mod_fld.t:90
character(len=40) fld_fluxlimiter
flux limiter choice
Definition mod_fld.t:38
subroutine, public fld_get_radflux(w, x, ixil, ixol, rad_flux, fl)
Calculate Radiation Flux NOTE: only for diagnostics purposes (w conservative on entry) This returns c...
Definition mod_fld.t:641
character(len=40) fld_opal_table
Definition mod_fld.t:36
subroutine, public fld_get_opacity_prim(w, x, ixil, ixol, fld_kappa, fl)
Sets the opacity in the w-array by calling mod_opal_opacity NOTE: assumes primitives in w NOTE: assum...
Definition mod_fld.t:462
double precision, public fld_boost_dt
Definition mod_fld.t:30
subroutine get_and_check_egas_erad_from_conserved(w, ixil, ixol, e_gas, e_rad)
Definition mod_fld.t:722
logical fld_slowsteps
handling ramp-up phase with explicit diffusion dt limit, slowly boosted
Definition mod_fld.t:29
subroutine evaluate_diffterm_onegrid(ixil, ixol, w, x)
inplace update of psa==>F_im(psa)
Definition mod_fld.t:928
double precision function ddpolynomial_bisection(e_gas, c0, c1)
Evaluate second derivative of polynomial at argument e_gas.
Definition mod_fld.t:1154
subroutine, public fld_get_fluxlimiter_prim(w, x, ixil, ixol, fld_lambda, fld_r, nth, fl)
This subroutine calculates flux limiter lambda according to fld_fluxlimiter It also calculates fld_R ...
Definition mod_fld.t:558
double precision, public fld_kappa0
Opacity value when using constant opacity.
Definition mod_fld.t:22
subroutine newton_method(e_gas, c0, c1)
Find the root of the 4th degree polynomial using the Newton method.
Definition mod_fld.t:1074
double precision function polynomial_bisection(e_gas, c0, c1)
Evaluate polynomial at argument e_gas.
Definition mod_fld.t:1134
subroutine, public add_fld_rad_force(qdt, ixil, ixol, wct, wctprim, w, x, qsourcesplit, active, fl)
w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO This subroutine handles th...
Definition mod_fld.t:220
character(len=40) fld_opacity_law
switches for opacity
Definition mod_fld.t:35
character(len=40) fld_interaction_method
Which method to find the root for the energy interaction polynomial.
Definition mod_fld.t:44
subroutine, public fld_set_mg_bounds
Set the boundaries for the diffusion of E.
Definition mod_fld.t:173
subroutine, public fld_get_radpress(w, x, ixil, ixol, rad_pressure, fl)
Returns Radiation Pressure as tensor NOTE: w is primitive on entry.
Definition mod_fld.t:501
logical fld_debug
Definition mod_fld.t:33
logical fld_radforce_split
source split for energy interact and radforce:
Definition mod_fld.t:18
subroutine, public fld_implicit_update(dtfactor, qdt, qtc, psa, psb, fl)
Calling all subroutines to perform the multigrid method Communicates rad_e and diff_coeff to multigri...
Definition mod_fld.t:761
subroutine, public fld_get_fluxlimiter(w, x, ixil, ixol, fld_lambda, fld_r, nth, fl)
This subroutine calculates flux limiter lambda according to fld_fluxlimiter It also calculates fld_R ...
Definition mod_fld.t:535
double precision function dpolynomial_bisection(e_gas, c0, c1)
Evaluate first derivative of polynomial at argument e_gas.
Definition mod_fld.t:1144
logical fld_tiring_explicit
switch to handle photon tiring explicit or implicit
Definition mod_fld.t:20
subroutine, public fld_get_diffcoef_central(w, x, ixil, ixol, fl)
Calculates cell-centered diffusion coefficient to be used in multigrid.
Definition mod_fld.t:965
integer i_diff_mg
diffusion coefficient for multigrid method
Definition mod_fld.t:40
subroutine bisection_method(e_gas, c0, c1)
Find the root of the 4th degree polynomial using the bisection method.
Definition mod_fld.t:1013
subroutine, public fld_evaluate_implicit(qtc, psa, fl)
inplace update of psa==>F_im(psa)
Definition mod_fld.t:907
subroutine, public fld_init()
Initialising FLD-module Read opacities Initialise Multigrid and adimensionalise kappa.
Definition mod_fld.t:112
subroutine halley_method(e_gas, c0, c1)
Find the root of the 4th degree polynomial using the Halley method.
Definition mod_fld.t:1103
integer nth_for_diff_mg
diffusion coefficient stencil control
Definition mod_fld.t:42
Module with basic grid data structures.
Definition mod_forest.t:2
Module with geometry-related routines (e.g., divergence, curl)
Definition mod_geometry.t:2
subroutine gradient(q, ixil, ixol, idir, gradq, nth_in)
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 arad_norm
Normalised radiation constant.
double precision unit_density
Physical scaling factor for density.
double precision unit_opacity
Physical scaling factor for Opacity.
integer, parameter unitpar
file handle for IO
double precision global_time
The global simulation time.
integer, dimension(3, 3) kr
Kronecker delta tensor.
integer it
Number of time steps taken.
integer it_init
initial iteration count
integer, dimension(:, :), allocatable typeboundary
Array indicating the type of boundary condition per variable and per physical boundary.
integer, parameter ndim
Number of spatial dimensions for grid variables.
character(len=std_len), dimension(:), allocatable par_files
Which par files are used as input.
integer mype
The rank of the current MPI task.
double precision courantpar
The Courant (CFL) number used for the simulation.
integer ixm
the mesh range of a physical block without ghost cells
double precision, dimension(:), allocatable, parameter d
logical slab
Cartesian geometry or not.
integer slowsteps
If > 1, then in the first slowsteps-1 time steps dt is reduced by a factor .
integer, parameter bc_periodic
integer, parameter bc_special
boundary condition types
double precision c_norm
Normalised speed of light.
double precision, dimension(:,:), allocatable rnode
Corner coordinates.
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
logical fix_small_values
fix small values with average or replace methods
double precision, dimension(^nd) dxlevel
store unstretched cell size of current level
logical use_multigrid
Use multigrid (only available in 2D and 3D)
logical slab_uniform
uniform Cartesian geometry or not (stretched Cartesian)
integer refine_max_level
Maximal number of AMR levels.
integer max_blocks
The maximum number of grid blocks in a processor.
logical check_small_values
check and optionally fix unphysical small values (density, gas pressure)
Module for reading input and writing output.
subroutine get_global_minima(wmin, psa)
Compute global minima of iw variables over the leaves of the grid.
subroutine get_global_maxima(wmax, psa)
Compute global maxima of iw variables over the leaves of the grid.
Module to couple the octree-mg library to AMRVAC. This file uses the VACPP preprocessor,...
type(mg_t) mg
Data structure containing the multigrid tree.
subroutine mg_copy_to_tree(iw_from, iw_to, restrict, restrict_gc, factor, state_from)
Copy a variable to the multigrid tree, including a layer of ghost cells.
subroutine mg_copy_from_tree_gc(iw_from, iw_to, state_to)
Copy from multigrid tree with one layer of ghost cells. Corner ghost cells are not used/set.
This module reads in Rosseland-mean opacities from OPAL tables. Table opacity values are given in bas...
subroutine, public init_opal_table(tabledir, set_custom_tabledir)
This subroutine is called when the FLD radiation module is initialised. The OPAL tables for different...
subroutine, public set_opal_opacity(rho, temp, kappa)
This subroutine calculates the opacity for a given temperature-density structure. Opacities are read ...
This module defines the procedures of a physics module. It contains function pointers for the various...
Definition mod_physics.t:4
procedure(sub_convert), pointer phys_to_primitive
Definition mod_physics.t:52
integer phys_wider_stencil
To use wider stencils in flux calculations. A value of 1 will extend it by one cell in both direction...
Definition mod_physics.t:17
procedure(sub_set_mg_bounds), pointer phys_set_mg_bounds
Definition mod_physics.t:50
procedure(sub_get_csrad2), pointer phys_get_csrad2
Definition mod_physics.t:80
Module with all the methods that users can customize in AMRVAC.
procedure(special_opacity), pointer usr_special_opacity
procedure(special_diffcoef), pointer usr_special_diffcoef
procedure(special_fluxlimiter), pointer usr_special_fluxlimiter
procedure(special_mg_bc), pointer usr_special_mg_bc
integer function var_set_extravar(name_cons, name_prim, ix)
Set extra variable in w, which is not advected and has no boundary conditions. This has to be done af...
Radiation fluid object: gas-EoS callbacks the FLD module needs, wired by the physics module at link t...
Definition mod_fld.t:60