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