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