MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_rt_sc_solvers.t
Go to the documentation of this file.
1!=============================================================================
2!> Solvers of the short-characteristics (SC) radiative transfer and their dispatch
3!>
4!> rt_sc_solve runs once per step. It sweeps every ray through every band on rt_sc_amr_level and
5!> leaves the heating Q in i_qrad and the vertical flux Fy in i_frad. The pipelined KBA wavefront
6!> is the default. Its sweep corners run at once, each in its own intensity slot, and the rays of
7!> a corner follow one another through the wavefront. The ray-by-ray wavefront passes one ray at
8!> a time, agrees with it to round-off, and is the simplest to read. In both, each rank
9!> sweeps only its own band blocks, one diagonal of blocks at a time, with a getbc of the ray
10!> intensity between diagonals, so that no rank holds the whole band. The replicated gather
11!> assembles chi and B of the whole band on every rank and is kept as the reference. It differs
12!> from the wavefront only where rays cross a periodic seam
13!>
14!> The loops over block cells are written along (hdim, updim) through hv, so that one loop
15!> serves either stratification axis
16!>
17!> Structure:
18!> A. dispatch rt_sc_solve and the validation rt_sc_validate_pipe;
19!> B. gather rt_sc_solve_gather and rt_grid_index;
20!> C. band blocks rt_band_blocks, which lists the band blocks and caches chi and B, with
21!> rt_tau_tops and rt_block_tau for tau from the band top, which
22!> rt_sc_emergent_tau1 shares;
23!> D. wave stages rt_wave_begin, rt_wave_band_open, rt_wave_band_close, which forms Q, and
24!> rt_wave_end, with rt_add_moments and rt_exchange;
25!> E. block work rt_seam_read and rt_seam_write for the periodic seam, rt_inflow_ghosts for
26!> the band-end and edge inflows, and rt_sweep_one;
27!> F. schedules rt_sc_solve_wave and rt_sc_solve_wave_pipe, with wave_stage
28!>
29!> Jack Jenkins (10.2026); More or less finished in v1 state for amrvac4.0
30!=============================================================================
33 implicit none
34
35 public :: rt_sc_solve
36
37contains
38
39 !> Collective, once per step. For each band on rt_sc_amr_level it sweeps all rays and forms the
40 !> heating Q, the thin/thick blend with the tau^2 taper, in i_qrad, which is zero elsewhere,
41 !> together with Fy, and Iout on the gather path. rt_sc_add_source applies the energy every
42 !> stage. The gather path is for comparison only, see rt_sc_solve_gather
43 subroutine rt_sc_solve()
46 double precision :: tw(3)
47{^iftwod
48 if(.not.rt_sc_active) return
49 if(rt_sc_valpipe .and. rt_sc_distributed) then
51 return
52 end if
53 call timer_open(tw)
54 if(rt_sc_distributed) then
55 if(rt_sc_pipeline) then
57 else
58 call rt_sc_solve_wave()
59 end if
60 rt_wt_wave = rt_wt_wave + (mpi_wtime()-tw(1))
62 if(rt_sc_debug .and. mype==0 .and. mod(rt_nsolve,10)==0) write(*,'(a,i5,a,f9.3,a)') &
63 ' RT wave nsolve=',rt_nsolve,' cumulative wave-solve time=',rt_wt_wave,' s'
64 else
66 end if
68 ! The per-rank wall time, which includes the pipeline waits that show the imbalance
69 if(lb_diagnose) lb_rt_accum = mpi_wtime() - tw(1)
70}
71 end subroutine rt_sc_solve
72
73 !> Replicated gather, the original implementation, kept for comparison and not for production.
74 !> Every rank holds chi and B of the whole band, the rays are shared out over the node-local
75 !> ranks, and the moments are reduced within the node. Its memory grows with the whole band on
76 !> every rank, and its periodic seam takes a vacuum inflow for rays swept column by column
77 !> (sc_sweep_2d). Use KBA, pipelined or not
78 subroutine rt_sc_solve_gather()
80 use mod_comm_lib, only: mpistop
81 {^iftwod
82 double precision :: chi_l(ixG^T), b_l(ixG^T), tw
83 double precision, allocatable :: fill_g(:,:)
84 integer :: iigrid, igrid, ix1, ix2, ig, jg, k, ierr, ib
85 if(.not.rt_sc_active) return
86 if(.not.rt_sc_ready) call mpistop("rt_sc_solve_gather: rt_sc_setup not called")
87 if(i_qrad < 0) call mpistop("rt_sc_solve_gather: i_qrad not registered")
88 if(.not.associated(rt_sc_chib)) call mpistop("rt_sc_solve_gather: opacity callback not set")
89
90 ! The node-local and leader communicators are built on the first call. Every rank joins its
91 ! node communicator, and only the node leaders join the leader communicator
92 if(rt_node_comm == -1) then
93 call mpi_comm_split_type(icomm, mpi_comm_type_shared, 0, mpi_info_null, rt_node_comm, ierr)
94 call mpi_comm_rank(rt_node_comm, rt_node_rank, ierr)
95 call mpi_comm_size(rt_node_comm, rt_node_size, ierr)
96 if(rt_node_rank == 0) then
97 call mpi_comm_split(icomm, 0, mype, rt_lead_comm, ierr)
98 else
99 call mpi_comm_split(icomm, mpi_undefined, mype, rt_lead_comm, ierr)
100 end if
101 end if
102
103 nxg = rg_nh; dxg = rg_dh; dyg = rg_dv
104
105 ! Q is zeroed everywhere first, so the cells outside the bands receive no heating
106 do iigrid=1,igridstail; igrid=igrids(iigrid)
107 ps(igrid)%w(ixg^t,i_qrad) = 0.0d0
108 end do
109
110 do ib = 1, rt_sc_nband
111 ! Each band has its own incoming conditions and its own number of rows
112 bc_lo_thermal = (trim(rt_sc_bc_min(ib)) == 'thermal')
113 bc_hi_thermal = (trim(rt_sc_bc_max(ib)) == 'thermal')
114 nyg = band_nv(ib)
115 call rt_grid_alloc()
116 ! fill_g counts how many cells land on each grid point, which checks the AMR pin below
117 allocate(fill_g(nxg,nyg)); fill_g = 0.0d0
118 chi_g = 0.0d0; b_g = 0.0d0
119 tw = mpi_wtime()
120
121 ! Each rank writes chi and B of its own level-L band cells into the global grid
122 do iigrid=1,igridstail; igrid=igrids(iigrid)
123 if(node(plevel_,igrid) /= rt_sc_amr_level) cycle
124 call rt_sc_chib(ixg^ll, ixm^ll, ps(igrid)%w, ps(igrid)%x, chi_l, b_l)
125 do ix2=ixmlo2,ixmhi2; do ix1=ixmlo1,ixmhi1
126 if(.not.rt_grid_index(ps(igrid)%x(ix1,ix2,hdim), ps(igrid)%x(ix1,ix2,updim), ib, ig, jg)) cycle
127 chi_g(ig,jg) = chi_l(ix1,ix2); b_g(ig,jg) = b_l(ix1,ix2); fill_g(ig,jg) = 1.0d0
128 end do; end do
129 end do
130 ! Each point is written by one rank and is zero on all the others, so summing within the
131 ! node, then over the leaders, and broadcasting within the node gives every rank the field
132 call rt_allreduce_chunk(chi_g, nxg, nyg, rt_node_comm)
133 call rt_allreduce_chunk(b_g, nxg, nyg, rt_node_comm)
134 call rt_allreduce_chunk(fill_g, nxg, nyg, rt_node_comm)
135 if(rt_lead_comm /= mpi_comm_null) then
136 call rt_allreduce_chunk(chi_g, nxg, nyg, rt_lead_comm)
137 call rt_allreduce_chunk(b_g, nxg, nyg, rt_lead_comm)
138 call rt_allreduce_chunk(fill_g, nxg, nyg, rt_lead_comm)
139 end if
140 if(rt_node_size > 1) then
141 call mpi_bcast(chi_g, nxg*nyg, mpi_double_precision, 0, rt_node_comm, ierr)
142 call mpi_bcast(b_g, nxg*nyg, mpi_double_precision, 0, rt_node_comm, ierr)
143 call mpi_bcast(fill_g, nxg*nyg, mpi_double_precision, 0, rt_node_comm, ierr)
144 end if
145 ! Every grid point must have been filled exactly once. Anything else means the band is not
146 ! one complete level and the AMR pin has failed
147 if(minval(fill_g) /= 1.0d0 .or. maxval(fill_g) /= 1.0d0) then
148 if(mype==0) print *,'rt_sc_solve_gather: band',ib,' not a complete single level ', &
149 rt_sc_amr_level,' (minfill',minval(fill_g),' maxfill',maxval(fill_g),')'
150 call mpistop("rt_sc_solve_gather: RT band is not a complete single AMR level")
151 end if
152 deallocate(fill_g)
153 call sc_pad_grid()
154 rt_wt_gath = rt_wt_gath + (mpi_wtime()-tw); tw = mpi_wtime()
155
156 ! Without scattering each ray is one independent sweep, so the rays are shared out over the
157 ! node-local ranks and the moments summed within the node. Each sweep starts from zero
158 ! intensity, so the sum depends on the order and the number of ranks only through round-off
159 jm_g = 0.0d0; fx_g = 0.0d0; fy_g = 0.0d0
160 do k = rt_node_rank+1, rt_sc_nray, rt_node_size
161 call sc_sweep_2d(mu_h(k), mu_v(k))
162 ! J = sum_k w_k I_k, Fx = sum_k w_k mu_h,k I_k, Fy = sum_k w_k mu_v,k I_k
163 jm_g = jm_g + wmu(k)*ii_g
164 fx_g = fx_g + wmu(k)*mu_h(k)*ii_g
165 fy_g = fy_g + wmu(k)*mu_v(k)*ii_g
166 end do
167 rt_wt_swp = rt_wt_swp + (mpi_wtime()-tw); tw = mpi_wtime()
168 if(rt_node_size>1) then
169 call rt_allreduce_chunk(jm_g, nxg, nyg, rt_node_comm)
170 call rt_allreduce_chunk(fx_g, nxg, nyg, rt_node_comm)
171 call rt_allreduce_chunk(fy_g, nxg, nyg, rt_node_comm)
172 end if
173 rt_wt_red = rt_wt_red + (mpi_wtime()-tw); tw = mpi_wtime()
174 ! F = c sum_k w_k mu_k I_k
175 fx_g = c_norm*fx_g; fy_g = c_norm*fy_g
176 if(rt_sc_flux_blend) then
177 call rt_sc_heat_blend()
178 else
179 ! Q = c chi (J - B)
180 q_g = c_norm*chi_g*(jm_g - b_g)
181 end if
182 ! A vertical ray swept afterwards gives the emergent intensity Iout
183 call sc_sweep_2d(0.0d0, 1.0d0)
184
185 ! Q, Fy and Iout go back to the band's level-L cells through the same index map
186 do iigrid=1,igridstail; igrid=igrids(iigrid)
187 if(node(plevel_,igrid) /= rt_sc_amr_level) cycle
188 do ix2=ixmlo2,ixmhi2; do ix1=ixmlo1,ixmhi1
189 if(.not.rt_grid_index(ps(igrid)%x(ix1,ix2,hdim), ps(igrid)%x(ix1,ix2,updim), ib, ig, jg)) cycle
190 ps(igrid)%w(ix1,ix2,i_qrad) = q_g(ig,jg)
191 if(i_frad>0) ps(igrid)%w(ix1,ix2,i_frad) = fy_g(ig,jg)
192 if(i_iout>0) ps(igrid)%w(ix1,ix2,i_iout) = ii_g(ig,jg)
193 end do; end do
194 end do
195 rt_wt_scat = rt_wt_scat + (mpi_wtime()-tw)
196 end do
197 rt_nsolve = rt_nsolve + 1
198 if(rt_sc_debug .and. mype==0 .and. mod(rt_nsolve,10)==0) &
199 write(*,'(a,i5,4(a,f8.3),a,f8.3,a)') ' RT timing nsolve=',rt_nsolve, &
200 ' | gather+fieldreduce=',rt_wt_gath,' sweep=',rt_wt_swp,' momentreduce=',rt_wt_red, &
201 ' blend+scatter=',rt_wt_scat,' | total=',rt_wt_gath+rt_wt_swp+rt_wt_red+rt_wt_scat,' s (cumulative)'
202 }
203 end subroutine rt_sc_solve_gather
204
205 !> Indices (ig, jg) on the replicated grid of band ib of the cell centred at xh along hdim and xv
206 !> along updim, false when the cell lies outside the band
207 logical function rt_grid_index(xh, xv, ib, ig, jg) result(inside)
208 double precision, intent(in) :: xh, xv
209 integer, intent(in) :: ib
210 integer, intent(out) :: ig, jg
211 inside = .false.
212 if(xv <= band_lo_c(ib) .or. xv >= band_hi_c(ib)) return
213 ! ig = nint((x - x_lo)/dx + 1/2), jg = nint((y - y_band)/dy + 1/2)
214 ig = nint((xh - rg_hlo)/rg_dh + 0.5d0)
215 jg = nint((xv - band_lo_c(ib))/rg_dv + 0.5d0)
216 inside = ig >= 1 .and. ig <= rg_nh .and. jg >= 1 .and. jg <= band_nv(ib)
217 end function rt_grid_index
218
219 {^iftwod
220 !> Validation of the pipelined KBA. The ray-by-ray Q is copied into i_iout and the pipelined Q
221 !> left in i_qrad, and the largest difference is printed relative to the largest Q, both over
222 !> the whole band and away from the seam blocks, the outermost x-block on either side. Both
223 !> solves start from the same seam inflow, since the first one hands its own on to the second
226 integer :: iigrid, igrid, ierr, ix1, ix2
227 double precision :: dd, dmx, qmx, dmi, xedge, gbuf(3), gout(3)
228 double precision, allocatable :: seamL0(:,:), seamR0(:,:)
229 logical :: had_seam
230 had_seam = allocated(rt_sc_seaml_prev)
231 if(had_seam) then
232 seaml0 = rt_sc_seaml_prev; seamr0 = rt_sc_seamr_prev
233 end if
234 call rt_sc_solve_wave()
235 if(allocated(rt_sc_seaml_prev)) then
236 if(had_seam) then
237 rt_sc_seaml_prev = seaml0; rt_sc_seamr_prev = seamr0
238 else
239 rt_sc_seaml_prev = 0.0d0; rt_sc_seamr_prev = 0.0d0
240 end if
241 end if
242 do iigrid=1,igridstail; igrid=igrids(iigrid)
243 ps(igrid)%w(ixg^t,i_iout) = ps(igrid)%w(ixg^t,i_qrad)
244 end do
246 dmx=0.0d0; qmx=0.0d0; dmi=0.0d0
247 xedge = block_nx1*(xprobmax1-xprobmin1)/dble(domain_nx1)
248 do iigrid=1,igridstail; igrid=igrids(iigrid)
249 qmx=max(qmx, maxval(dabs(ps(igrid)%w(ixm^t,i_qrad))))
250 do ix2=ixmlo2,ixmhi2; do ix1=ixmlo1,ixmhi1
251 dd=dabs(ps(igrid)%w(ix1,ix2,i_qrad)-ps(igrid)%w(ix1,ix2,i_iout))
252 dmx=max(dmx,dd)
253 if(ps(igrid)%x(ix1,ix2,1) > xprobmin1+xedge .and. &
254 ps(igrid)%x(ix1,ix2,1) < xprobmax1-xedge) dmi=max(dmi,dd)
255 end do; end do
256 end do
257 ! rel = max abs(Q_pipe - Q_wave)/max abs(Q)
258 gbuf = (/ dmx, qmx, dmi /)
259 call mpi_allreduce(gbuf, gout, 3, mpi_double_precision, mpi_max, icomm, ierr)
260 if(mype==0) write(*,'(a,es12.5,a,es12.5,a,es12.5,a,es12.5)') &
261 ' rt_valpipe rel=',gout(1)/max(gout(2),1.0d-300),' rel(x-interior,no seam)=', &
262 gout(3)/max(gout(2),1.0d-300),' max|dQ|=',gout(1),' max|Q|=',gout(2)
263 end subroutine rt_sc_validate_pipe
264
265 !> List the blocks of this rank that lie in band ib, with their block position (bi, bj), cache chi
266 !> and B on them, ghosts included, and check that the band is one complete level
267 subroutine rt_band_blocks(ib)
269 use mod_comm_lib, only: mpistop
270 integer, intent(in) :: ib
271 integer :: iigrid, igrid, iblk, ierr
272 integer, allocatable :: cover(:,:)
273 ! The band holds whole blocks, since rt_sc_setup snapped band_hi_c to a block face, and its
274 ! longest diagonal p+q is rg_nbh + wv_nbxv
275 wv_nbxv = band_nbv(ib)
276 wv_dmax = rg_nbh + wv_nbxv
277 if(allocated(wv_ig)) deallocate(wv_ig,wv_bi,wv_bj)
278 allocate(wv_ig(max(igridstail,1)),wv_bi(max(igridstail,1)),wv_bj(max(igridstail,1)))
279 wv_nblk = 0
280 do iigrid=1,igridstail; igrid=igrids(iigrid)
281 if(node(plevel_,igrid)/=rt_sc_amr_level) cycle
282 if(rnode(rpxmin0_+updim,igrid) < band_lo_c(ib)-0.5d0*rg_dv) cycle
283 if(rnode(rpxmin0_+updim,igrid)+rg_bv*rg_dv > band_hi_c(ib)+0.5d0*rg_dv) cycle
284 wv_nblk = wv_nblk + 1
285 wv_ig(wv_nblk) = igrid
286 wv_bi(wv_nblk) = nint((rnode(rpxmin0_+hdim,igrid) -rg_hlo)/(rg_bh*rg_dh)) + 1
287 wv_bj(wv_nblk) = nint((rnode(rpxmin0_+updim,igrid)-band_lo_c(ib))/(rg_bv*rg_dv)) + 1
288 end do
289 ! The caches only grow and are otherwise reused
290 if(allocated(rtw_chib)) then
291 if(size(rtw_chib,3) < max(wv_nblk,1)) deallocate(rtw_chib,rtw_bb,rtw_fx)
292 end if
293 if(.not.allocated(rtw_chib)) allocate(rtw_chib(ixglo1:ixghi1,ixglo2:ixghi2,max(wv_nblk,1)), &
294 rtw_bb(ixglo1:ixghi1,ixglo2:ixghi2,max(wv_nblk,1)), &
295 rtw_fx(ixglo1:ixghi1,ixglo2:ixghi2,max(wv_nblk,1)))
296 ! Every (bi, bj) of the band must be owned exactly once, or the AMR pin has failed
297 allocate(cover(rg_nbh, wv_nbxv)); cover = 0
298 do iblk = 1, wv_nblk
299 igrid = wv_ig(iblk)
300 call rt_sc_chib(ixg^ll, ixg^ll, ps(igrid)%w, ps(igrid)%x, rtw_chib(:,:,iblk), rtw_bb(:,:,iblk))
301 ! Beyond the band ends chi and B take the edge values, as in the gather, so that the deep
302 ! row thermalises. A ghost extrapolation would turn the large deep chi into spurious heating
303 if(wv_bj(iblk)==1) call copy_edge_row(iglo_v, imlo_v-1, imlo_v)
304 if(wv_bj(iblk)==wv_nbxv) call copy_edge_row(imhi_v+1, ighi_v, imhi_v)
305 cover(wv_bi(iblk), wv_bj(iblk)) = cover(wv_bi(iblk), wv_bj(iblk)) + 1
306 end do
307 ! The coverage is summed over the ranks before it is checked
308 call mpi_allreduce(mpi_in_place, cover, rg_nbh*wv_nbxv, mpi_integer, mpi_sum, icomm, ierr)
309 if(minval(cover) /= 1 .or. maxval(cover) /= 1) then
310 if(mype==0) print *,'rt_band_blocks: band',ib,' not a complete single level ', &
311 rt_sc_amr_level,' (mincover',minval(cover),' maxcover',maxval(cover),')'
312 call mpistop("rt_band_blocks: RT band is not a complete single AMR level")
313 end if
314 contains
315 !> Rows iv_lo to iv_hi of the chi and B caches of block iblk take row iv_src
316 subroutine copy_edge_row(iv_lo, iv_hi, iv_src)
317 integer, intent(in) :: iv_lo, iv_hi, iv_src
318 integer :: ih, iv, i1, i2, j1, j2
319 do iv = iv_lo, iv_hi
320 do ih = iglo_h, ighi_h
321 call hv(ih, iv, i1, i2)
322 call hv(ih, iv_src, j1, j2)
323 rtw_chib(i1,i2,iblk) = rtw_chib(j1,j2,iblk)
324 rtw_bb(i1,i2,iblk) = rtw_bb(j1,j2,iblk)
325 end do
326 end do
327 end subroutine copy_edge_row
328 end subroutine rt_band_blocks
329
330 !> Tau at the top face of every block row of the current band, per global column along hdim.
331 !> Each block adds the optical thickness of its columns, one allreduce assembles the table, and
332 !> the top of a row lies below all the rows above it
333 subroutine rt_tau_tops(tautop)
335 double precision, intent(out) :: tautop(rg_nh,wv_nbxv)
336 double precision :: colthk(rg_nh,wv_nbxv), s
337 integer :: iblk, ih, iv, i1, i2, ig, bj, jb, ierr
338 colthk = 0.0d0
339 do iblk = 1, wv_nblk
340 do ih = imlo_h, imhi_h
341 ig = (wv_bi(iblk)-1)*rg_bh + (ih - imlo_h + 1)
342 ! colthk = sum_j chi_j dy down the block column
343 s = 0.0d0
344 do iv = imlo_v, imhi_v
345 call hv(ih, iv, i1, i2)
346 s = s + rtw_chib(i1,i2,iblk)
347 end do
348 colthk(ig,wv_bj(iblk)) = s*rg_dv
349 end do
350 end do
351 call mpi_allreduce(mpi_in_place, colthk, rg_nh*wv_nbxv, mpi_double_precision, mpi_sum, icomm, ierr)
352 do bj = 1, wv_nbxv
353 ! tau_top(bj) = sum of colthk(jb) over the rows jb > bj
354 tautop(:,bj) = 0.0d0
355 do jb = bj+1, wv_nbxv; tautop(:,bj) = tautop(:,bj) + colthk(:,jb); end do
356 end do
357 end subroutine rt_tau_tops
358
359 !> Tau from the band top to every cell centre of block iblk, integrated down from the top face
360 !> of the block
361 subroutine rt_block_tau(iblk, tautop, taub)
363 integer, intent(in) :: iblk
364 double precision, intent(in) :: tautop(rg_nh,wv_nbxv)
365 double precision, intent(out) :: taub(ixG^T)
366 integer :: ih, iv, i1, i2, j1, j2, ig
367 do ih = imlo_h, imhi_h
368 ig = (wv_bi(iblk)-1)*rg_bh + (ih - imlo_h + 1)
369 ! tau = tau_top + chi dy/2 in the top cell, tau_j = tau_(j+1) + (chi_(j+1) + chi_j) dy/2
370 call hv(ih, imhi_v, i1, i2)
371 taub(i1,i2) = tautop(ig,wv_bj(iblk)) + 0.5d0*rtw_chib(i1,i2,iblk)*rg_dv
372 do iv = imhi_v-1, imlo_v, -1
373 call hv(ih, iv, i1, i2)
374 call hv(ih, iv+1, j1, j2)
375 taub(i1,i2) = taub(j1,j2) + 0.5d0*(rtw_chib(j1,j2,iblk)+rtw_chib(i1,i2,iblk))*rg_dv
376 end do
377 end do
378 end subroutine rt_block_tau
379
380 !> Prologue of both KBA schedules on level rt_sc_amr_level. It checks the setup, sets the
381 !> x-distribution mode, refreshes the hydro ghosts, allocates the intensity state and the seam
382 !> buffers, and builds the nslot wavefront datatype. Each rank sweeps only its own band blocks,
383 !> ordered per ray by the diagonal p+q counted from the ray's upwind corner, with a getbc between
384 !> diagonals, so every cell is swept once per ray
385 subroutine rt_wave_begin(nslot)
387 use mod_comm_lib, only: mpistop
389 use mod_variables, only: iwstart, nwgc
390 integer, intent(in) :: nslot
391 if(.not.rt_sc_ready) call mpistop("rt_wave_begin: rt_sc_setup not called")
392 if(i_isw < 0) call mpistop("rt_wave_begin: wavefront slot not reserved")
393 if(i_qrad < 0 .or. i_frad < 0) call mpistop("rt_wave_begin: the wave needs i_qrad and i_frad")
394 ! With one block across x, sc_sweep_block wraps in place. With several, the x-upwind values
395 ! come from the neighbour's ghosts, the diagonal widens, and the periodic seam takes the
396 ! lagged wrap
397 wv_xwrap = (rg_nbh == 1)
398 if(.not.wv_xwrap .and. mype==0 .and. .not.rt_sc_seam_warned) then
399 write(*,*) 'rt_wave_begin: periodic seam closed by the lagged wrap'
400 rt_sc_seam_warned = .true.
401 end if
402 ! The hydro ghosts are refreshed so that chi and B can be evaluated on the ghost ring
403 call getbc(global_time, 0.0d0, ps, iwstart, nwgc)
404 call rt_sc_alloc_state()
405 ! A non-periodic hdim has inflow edges and therefore no seam
406 wv_seam = rt_sc_seam_on .and. .not.wv_xwrap .and. bc_h_periodic
407 if(wv_seam) call rt_sc_seam_alloc(rg_nv, rt_sc_nray)
408 ! From here on getbc of ps_rt exchanges only the nslot intensity columns
409 call create_bc_mpi_datatype(i_isw, nslot)
410 end subroutine rt_wave_begin
411
412 !> Open band ib. It lists and caches the band blocks and zeroes the accumulators
413 subroutine rt_wave_band_open(ib)
415 integer, intent(in) :: ib
416 integer :: iblk, igrid
417 ! Each band has its own incoming conditions at the ends
418 bc_lo_thermal = (trim(rt_sc_bc_min(ib)) == 'thermal')
419 bc_hi_thermal = (trim(rt_sc_bc_max(ib)) == 'thermal')
420 call rt_band_blocks(ib)
421 do iblk = 1, wv_nblk
422 igrid = wv_ig(iblk)
423 ! i_qrad accumulates J and later holds Q, while i_frad and rtw_fx accumulate Fy and Fx
424 ps(igrid)%w(ixg^t,i_qrad) = 0.0d0
425 ps(igrid)%w(ixg^t,i_frad) = 0.0d0
426 rtw_fx(:,:,iblk) = 0.0d0
427 end do
428 end subroutine rt_wave_band_open
429
430 !> Close the band. The fluxes are scaled by c_norm, Q is formed as the imbalance or as the -div F
431 !> blend with the tau^2 taper, and the nslot wavefront datatype is restored
432 subroutine rt_wave_band_close(nslot)
435 integer, intent(in) :: nslot
436 integer :: iblk, igrid, ix1, ix2, ih, iv, i1, i2
437 double precision :: taub(ixG^T), dfxdx, dfydy
438 double precision, allocatable :: tautop(:,:)
439 ! Fy = c sum_k w_k mu_v,k I_k, Fx = c sum_k w_k mu_h,k I_k
440 do iblk = 1, wv_nblk
441 igrid = wv_ig(iblk)
442 ps(igrid)%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_frad) = c_norm*ps(igrid)%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_frad)
443 rtw_fx(ixmlo1:ixmhi1,ixmlo2:ixmhi2,iblk) = c_norm*rtw_fx(ixmlo1:ixmhi1,ixmlo2:ixmhi2,iblk)
444 end do
445
446 if(.not.rt_sc_flux_blend) then
447 ! Without the blend Q is the imbalance, which is right in the thin limit but becomes stiff
448 ! and cancelling deep down
449 do iblk = 1, wv_nblk
450 igrid = wv_ig(iblk)
451 do ix2=ixmlo2,ixmhi2; do ix1=ixmlo1,ixmhi1
452 ! Q = c chi (J - B)
453 ps(igrid)%w(ix1,ix2,i_qrad) = c_norm*rtw_chib(ix1,ix2,iblk)* &
454 (ps(igrid)%w(ix1,ix2,i_qrad) - rtw_bb(ix1,ix2,iblk))
455 end do; end do
456 end do
457 return
458 end if
459
460 ! With the blend, Q = w c chi (J - B) + (1 - w)(-div F), where w = exp(-k tau) with tau
461 ! measured from the top, and the taper multiplies the result. dFx/dx needs Fx in the x
462 ! ghosts, which it reaches through the i_isw slot of ps_rt, and dFy/dy needs Fy in the y
463 ! ghosts from a getbc of i_frad
464 do iblk = 1, wv_nblk
465 ps_rt(wv_ig(iblk))%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_isw) = rtw_fx(ixmlo1:ixmhi1,ixmlo2:ixmhi2,iblk)
466 end do
467 call create_bc_mpi_datatype(i_isw, 1)
468 call rt_exchange(1)
469 call create_bc_mpi_datatype(i_frad, 1)
470 call getbc(global_time, 0.0d0, ps, i_frad, 1)
471 allocate(tautop(rg_nh,wv_nbxv))
472 call rt_tau_tops(tautop)
473 do iblk = 1, wv_nblk
474 igrid = wv_ig(iblk)
475 call rt_block_tau(iblk, tautop, taub)
476 do iv = imlo_v, imhi_v
477 do ih = imlo_h, imhi_h
478 call hv(ih, iv, i1, i2)
479 ! dFx/dx = (Fx_(i+1) - Fx_(i-1))/(2 dx), centred, since the ghosts hold the neighbour
480 ! block or the periodic image
481 dfxdx = (ps_rt(igrid)%w(i1+eh(1),i2+eh(2),i_isw) &
482 -ps_rt(igrid)%w(i1-eh(1),i2-eh(2),i_isw))/(2.0d0*rg_dh)
483 ! dFy/dy = (Fy_(j+1) - Fy_(j-1))/(2 dy), one-sided in the bottom and top rows of the
484 ! band, where the ghosts lie outside it
485 if(wv_bj(iblk)==1 .and. iv==imlo_v) then
486 dfydy = (ps(igrid)%w(i1+ev(1),i2+ev(2),i_frad)-ps(igrid)%w(i1,i2,i_frad))/rg_dv
487 else if(wv_bj(iblk)==wv_nbxv .and. iv==imhi_v) then
488 dfydy = (ps(igrid)%w(i1,i2,i_frad)-ps(igrid)%w(i1-ev(1),i2-ev(2),i_frad))/rg_dv
489 else
490 dfydy = (ps(igrid)%w(i1+ev(1),i2+ev(2),i_frad) &
491 -ps(igrid)%w(i1-ev(1),i2-ev(2),i_frad))/(2.0d0*rg_dv)
492 end if
493 ps(igrid)%w(i1,i2,i_qrad) = blend_heating(rtw_chib(i1,i2,iblk), &
494 ps(igrid)%w(i1,i2,i_qrad), rtw_bb(i1,i2,iblk), dfxdx+dfydy, taub(i1,i2))
495 end do
496 end do
497 end do
498 deallocate(tautop)
499 ! The datatype goes back to the nslot intensity columns for the next band
500 call create_bc_mpi_datatype(i_isw, nslot)
501 end subroutine rt_wave_band_close
502
503 !> Add the weighted intensity iib of ray k on block iblk to J in i_qrad, Fy in i_frad and Fx in
504 !> rtw_fx
505 subroutine rt_add_moments(iblk, k, iib)
507 integer, intent(in) :: iblk, k
508 double precision, intent(in) :: iib(ixG^T)
509 integer :: igrid
510 igrid = wv_ig(iblk)
511 ! J += w_k I_k, Fy += w_k mu_v,k I_k, Fx += w_k mu_h,k I_k
512 ps(igrid)%w(ixm^t,i_qrad) = ps(igrid)%w(ixm^t,i_qrad) + wmu(k)*iib(ixm^t)
513 ps(igrid)%w(ixm^t,i_frad) = ps(igrid)%w(ixm^t,i_frad) + wmu(k)*mu_v(k)*iib(ixm^t)
514 rtw_fx(ixm^t,iblk) = rtw_fx(ixm^t,iblk) + wmu(k)*mu_h(k)*iib(ixm^t)
515 end subroutine rt_add_moments
516
517 !> Ghost exchange of the nslot intensity columns of ps_rt from i_isw, with the physical
518 !> boundaries left alone, since the inflows are written separately
519 subroutine rt_exchange(nslot)
522 integer, intent(in) :: nslot
523 bcphys = .false.
524 call getbc(global_time, 0.0d0, ps_rt, i_isw, nslot)
525 bcphys = .true.
526 end subroutine rt_exchange
527
528 !> Epilogue. It hands the seam edges over to the next solve, restores the hydro datatype and
529 !> releases the block list
530 subroutine rt_wave_end()
533 use mod_variables, only: iwstart, nwgc
534 if(wv_seam) call rt_sc_seam_finalize(rg_nv, rt_sc_nray)
536 if(allocated(wv_ig)) deallocate(wv_ig,wv_bi,wv_bj)
537 end subroutine rt_wave_end
538
539 !> Seam read. At the domain edge where ray k enters, the wrapped upwind ghost column of the block
540 !> takes the outflow of the far edge from the last solve. A ray moving in +x enters through the
541 !> first block column and reads seamR, and one moving in -x enters through the last and reads
542 !> seamL
543 subroutine rt_seam_read(iib, igrid, ibi, sx, k)
545 double precision, intent(inout) :: iib(ixG^T)
546 integer, intent(in) :: igrid, ibi, sx, k
547 integer :: iv, i1, i2, gy0
548 ! gy0 is the global row offset of the block along updim
549 gy0 = nint((rnode(rpxmin0_+updim,igrid)-rg_vlo)/rg_dv)
550 if(ibi==1 .and. sx>0) then
551 do iv = imlo_v, imhi_v
552 call hv(imlo_h-1, iv, i1, i2); iib(i1,i2) = rt_sc_seamr_prev(gy0+(iv-imlo_v)+1,k)
553 end do
554 else if(ibi==rg_nbh .and. sx<0) then
555 do iv = imlo_v, imhi_v
556 call hv(imhi_h+1, iv, i1, i2); iib(i1,i2) = rt_sc_seaml_prev(gy0+(iv-imlo_v)+1,k)
557 end do
558 end if
559 end subroutine rt_seam_read
560
561 !> Seam write. At the far domain edge the outflow of ray k is kept for the next solve. A ray
562 !> moving in +x leaves through the last block column into seamR, and one moving in -x leaves
563 !> through the first into seamL
564 subroutine rt_seam_write(iib, igrid, ibi, sx, k)
566 double precision, intent(in) :: iib(ixG^T)
567 integer, intent(in) :: igrid, ibi, sx, k
568 integer :: iv, i1, i2, gy0
569 gy0 = nint((rnode(rpxmin0_+updim,igrid)-rg_vlo)/rg_dv)
570 if(ibi==rg_nbh .and. sx>0) then
571 do iv = imlo_v, imhi_v
572 call hv(imhi_h, iv, i1, i2); rt_sc_seamr_cur(gy0+(iv-imlo_v)+1,k) = iib(i1,i2)
573 end do
574 else if(ibi==1 .and. sx<0) then
575 do iv = imlo_v, imhi_v
576 call hv(imlo_h, iv, i1, i2); rt_sc_seaml_cur(gy0+(iv-imlo_v)+1,k) = iib(i1,i2)
577 end do
578 end if
579 end subroutine rt_seam_write
580
581 !> Inflow intensity in slot islot. The ghosts beyond the band ends, and beyond the horizontal
582 !> edges where hdim is not periodic, take B of the edge cell when that end is thermal and zero
583 !> when it is open. The band ends are interior rows of the domain, so getbc never fills them
584 subroutine rt_inflow_ghosts(islot)
586 integer, intent(in) :: islot
587 integer :: iblk, igrid
588 do iblk = 1, wv_nblk
589 igrid = wv_ig(iblk)
590 if(wv_bj(iblk)==1) call fill_rows(iglo_v, imlo_v-1, imlo_v, bc_lo_thermal)
591 if(wv_bj(iblk)==wv_nbxv) call fill_rows(imhi_v+1, ighi_v, imhi_v, bc_hi_thermal)
592 end do
593 if(bc_h_periodic) return
594 do iblk = 1, wv_nblk
595 igrid = wv_ig(iblk)
596 if(wv_bi(iblk)==1) call fill_columns(iglo_h, imlo_h-1, imlo_h, bc_hlo_thermal)
597 if(wv_bi(iblk)==rg_nbh) call fill_columns(imhi_h+1, ighi_h, imhi_h, bc_hhi_thermal)
598 end do
599 contains
600 !> Ghost rows iv_lo to iv_hi, from the edge row iv_src
601 subroutine fill_rows(iv_lo, iv_hi, iv_src, thermal)
602 integer, intent(in) :: iv_lo, iv_hi, iv_src
603 logical, intent(in) :: thermal
604 integer :: ih, iv, i1, i2, j1, j2
605 do iv = iv_lo, iv_hi
606 do ih = iglo_h, ighi_h
607 call hv(ih, iv, i1, i2)
608 call hv(ih, iv_src, j1, j2)
609 ps_rt(igrid)%w(i1,i2,islot) = merge(rtw_bb(j1,j2,iblk), 0.0d0, thermal)
610 end do
611 end do
612 end subroutine fill_rows
613 !> Ghost columns ih_lo to ih_hi, from the edge column ih_src
614 subroutine fill_columns(ih_lo, ih_hi, ih_src, thermal)
615 integer, intent(in) :: ih_lo, ih_hi, ih_src
616 logical, intent(in) :: thermal
617 integer :: ih, iv, i1, i2, j1, j2
618 do ih = ih_lo, ih_hi
619 do iv = iglo_v, ighi_v
620 call hv(ih, iv, i1, i2)
621 call hv(ih_src, iv, j1, j2)
622 ps_rt(igrid)%w(i1,i2,islot) = merge(rtw_bb(j1,j2,iblk), 0.0d0, thermal)
623 end do
624 end do
625 end subroutine fill_columns
626 end subroutine rt_inflow_ghosts
627
628 !> Sweep block iblk for ray k in place on iib. With updim = 1 the block is transposed around the
629 !> kernel, which expects its first index to be horizontal. The sweep time of each block is
630 !> accumulated for the load balancer
631 subroutine rt_sweep_one(iblk, k, iib)
633 integer, intent(in) :: iblk, k
634 double precision, intent(inout) :: iib(ixG^T)
635 double precision :: tii(ixGlo2:ixGhi2,ixGlo1:ixGhi1), t0
636 integer :: igrid
637 if(lb_automatic) t0 = mpi_wtime()
638 igrid = wv_ig(iblk)
639 if(updim==2) then
640 call sc_sweep_block(mu_h(k), mu_v(k), rg_dh, rg_dv, wv_xwrap, ixg^ll, ixm^ll, &
641 rtw_chib(:,:,iblk), rtw_bb(:,:,iblk), iib)
642 else
643 tii = transpose(iib)
644 call sc_sweep_block(mu_h(k), mu_v(k), rg_dh, rg_dv, wv_xwrap, &
645 ixglo2,ixglo1,ixghi2,ixghi1, ixmlo2,ixmlo1,ixmhi2,ixmhi1, &
646 transpose(rtw_chib(:,:,iblk)), transpose(rtw_bb(:,:,iblk)), tii)
647 iib = transpose(tii)
648 end if
649 if(lb_automatic) block_cost_rt(igrid) = block_cost_rt(igrid) + (mpi_wtime() - t0)
650 end subroutine rt_sweep_one
651
652 !> Position of a block counted from the upwind end of a row of n blocks, for a ray whose cosine
653 !> along that row has sign s. With s = 0 no block depends on its neighbour along the row, so
654 !> every block sits at the first position
655 pure integer function wave_stage(b, n, s)
656 integer, intent(in) :: b, n, s
657 wave_stage = merge(b, merge(n+1-b, 1, s<0), s>0)
658 end function wave_stage
659
660 !> Ray-by-ray KBA, with one ray at a time passing through the diagonal wavefront
661 subroutine rt_sc_solve_wave()
663 integer :: ib, k, sx, sy, d, iblk, igrid
664 double precision :: iib(ixG^T)
665 call rt_wave_begin(1)
666 do ib = 1, rt_sc_nband
667 call rt_wave_band_open(ib)
668 do k = 1, rt_sc_nray
669 ! The signs of the cosines fix the upwind corner the wavefront starts from
670 sx = ray_sign(mu_h(k)); sy = ray_sign(mu_v(k))
671 ! The intensity is zeroed for every ray, as in the gather, so the rays stay independent
672 do iblk = 1, wv_nblk
673 ps_rt(wv_ig(iblk))%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_isw) = 0.0d0
674 end do
675 ! Diagonal d holds the blocks with p + q = d, starting from the upwind corner at d = 2.
676 ! Before each diagonal the getbc hands the outflow faces of the last one to their
677 ! downstream neighbours, and the band-end and edge inflows are written over its ghosts
678 do d = 2, wv_dmax
679 call rt_exchange(1)
680 call rt_inflow_ghosts(i_isw)
681 do iblk = 1, wv_nblk
682 if(wave_stage(wv_bi(iblk),rg_nbh,sx) + wave_stage(wv_bj(iblk),wv_nbxv,sy) /= d) cycle
683 igrid = wv_ig(iblk)
684 ! The block is swept on a copy that holds its ghosts, with the seam inflow added at the
685 ! domain edge, and its interior is written back
686 iib = ps_rt(igrid)%w(:,:,i_isw)
687 if(wv_seam) call rt_seam_read(iib, igrid, wv_bi(iblk), sx, k)
688 call rt_sweep_one(iblk, k, iib)
689 ps_rt(igrid)%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_isw) = iib(ixmlo1:ixmhi1,ixmlo2:ixmhi2)
690 if(wv_seam) call rt_seam_write(iib, igrid, wv_bi(iblk), sx, k)
691 end do
692 end do
693 ! Once the ray has crossed the whole band, its weighted intensity adds to J, Fy and Fx
694 do iblk = 1, wv_nblk
695 call rt_add_moments(iblk, k, ps_rt(wv_ig(iblk))%w(:,:,i_isw))
696 end do
697 end do
698 call rt_wave_band_close(1)
699 end do
700 call rt_wave_end()
701 end subroutine rt_sc_solve_wave
702
703 !> Pipelined KBA. The direction-sign corners run together in batches of nisw slots, and within
704 !> each corner the rays follow one another through its wavefront. The result agrees with that
705 !> of rt_sc_solve_wave to round-off, since the moments are summed in another order
709 integer :: ib, k, sx, sy, iblk, igrid, islot, nslot
710 integer :: ng, gidx, g, jj, tt_clk, tmax, gbase, gend, gg
711 integer :: grp_sx(9), grp_sy(9), grp_n(9)
712 integer, allocatable :: grp_ray(:,:)
713 double precision :: iib(ixG^T)
714
715 ! The rays are grouped by the signs of (mu_h, mu_v), since the rays of one group share an
716 ! upwind corner and therefore one schedule. Counting the zero sign there are nine sign pairs,
717 ! so no more groups can occur
718 allocate(grp_ray(9, max(rt_sc_nray,1)))
719 ng = 0
720 do k = 1, rt_sc_nray
721 sx = ray_sign(mu_h(k)); sy = ray_sign(mu_v(k))
722 gidx = 0
723 do g = 1, ng
724 if(grp_sx(g)==sx .and. grp_sy(g)==sy) then; gidx = g; exit; end if
725 end do
726 if(gidx==0) then
727 ng = ng + 1
728 grp_sx(ng)=sx; grp_sy(ng)=sy; grp_n(ng)=0; gidx=ng
729 end if
730 grp_n(gidx) = grp_n(gidx) + 1
731 grp_ray(gidx, grp_n(gidx)) = k
732 end do
733 call rt_wave_begin(nisw)
734 do ib = 1, rt_sc_nband
735 call rt_wave_band_open(ib)
736 ! In group gg a block sits at stage p+q, and at clock tt_clk it sweeps ray
737 ! jj = tt_clk-(p+q)+2 of the group into slot i_isw+(gg-gbase). One getbc per clock
738 ! advances every front of the batch by one stage. The accumulators were zeroed when the band
739 ! opened, so they sum over the batches
740 do gbase = 1, ng, nisw
741 gend = min(gbase+nisw-1, ng)
742 nslot = gend-gbase+1
743 ! The slots of the batch start from zero intensity
744 do iblk = 1, wv_nblk
745 ps_rt(wv_ig(iblk))%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_isw:i_isw+nisw-1) = 0.0d0
746 end do
747 ! The last ray of a group reaches the last diagonal at clock grp_n + wv_dmax - 2, and the
748 ! batch runs until its longest group has finished
749 tmax = wv_dmax - 2
750 do gg = gbase, gend; tmax = max(tmax, grp_n(gg) + wv_dmax - 2); end do
751 ! The datatype is built once per batch, for its number of slots
752 call create_bc_mpi_datatype(i_isw, nslot)
753 do tt_clk = 1, tmax
754 ! One getbc moves every front, and the band-end and edge inflows are then written into
755 ! each slot
756 call rt_exchange(nslot)
757 do gg = gbase, gend
758 call rt_inflow_ghosts(i_isw+(gg-gbase))
759 end do
760 do gg = gbase, gend
761 islot = i_isw+(gg-gbase)
762 do iblk = 1, wv_nblk
763 ! jj = t - (p + q) + 2, the ray of the group that reaches this block at clock t. A
764 ! block sweeps at this clock only when that is a ray of the group
765 jj = tt_clk - (wave_stage(wv_bi(iblk),rg_nbh,grp_sx(gg)) &
766 + wave_stage(wv_bj(iblk),wv_nbxv,grp_sy(gg))) + 2
767 if(jj < 1 .or. jj > grp_n(gg)) cycle
768 k = grp_ray(gg, jj)
769 igrid = wv_ig(iblk)
770 ! The copy keeps the upwind ghosts from getbc, and its interior is cleared for ray k
771 iib = ps_rt(igrid)%w(:,:,islot)
772 iib(ixmlo1:ixmhi1,ixmlo2:ixmhi2) = 0.0d0
773 if(wv_seam) call rt_seam_read(iib, igrid, wv_bi(iblk), grp_sx(gg), k)
774 call rt_sweep_one(iblk, k, iib)
775 ! The interior goes back into the slot, the seam edge is kept, and the weighted
776 ! intensity adds to J, Fy and Fx straight away
777 ps_rt(igrid)%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,islot) = iib(ixmlo1:ixmhi1,ixmlo2:ixmhi2)
778 if(wv_seam) call rt_seam_write(iib, igrid, wv_bi(iblk), grp_sx(gg), k)
779 call rt_add_moments(iblk, k, iib)
780 end do
781 end do
782 end do
783 end do
784 call rt_wave_band_close(nisw)
785 end do
786 call rt_wave_end()
787 end subroutine rt_sc_solve_wave_pipe
788 }
789end module mod_rt_sc_solvers
subroutine copy_edge_row(iv_lo, iv_hi, iv_src)
Rows iv_lo to iv_hi of the chi and B caches of block iblk take row iv_src.
subroutine fill_rows(iv_lo, iv_hi, iv_src, thermal)
Ghost rows iv_lo to iv_hi, from the edge row iv_src.
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
update ghost cells of all blocks including physical boundaries
subroutine getbc(time, qdt, psb, nwstart, nwbc)
do update ghost cells of all blocks including physical boundaries
subroutine create_bc_mpi_datatype(nwstart, nwbc, nwfull)
This module contains definitions of global parameters and variables and some generic functions/subrou...
logical lb_diagnose
Per-rank load-balance timing diagnostic toggle (off by default). When .true., per-rank wall times are...
double precision global_time
The global simulation time.
integer icomm
The MPI communicator.
integer mype
The rank of the current MPI task.
integer ixm
the mesh range of a physical block without ghost cells
logical lb_automatic
Cost-weighted automatic load balancer toggle (off by default). When .true., the SFC partitioner cuts ...
double precision c_norm
Normalised speed of light.
double precision, dimension(:,:), allocatable rnode
Corner coordinates.
double precision, dimension(:), allocatable block_cost_rt
Per-step per-block cost of the short-characteristics sweep, indexed by igrid. Separate from block_cos...
double precision lb_rt_accum
Per-rank wall time spent in the SC radiative-transfer KBA sweep this step, for the lb_diagnose rank-t...
integer, dimension(:,:), allocatable node
Solvers of the short-characteristics (SC) radiative transfer and their dispatch.
subroutine rt_wave_band_close(nslot)
Close the band. The fluxes are scaled by c_norm, Q is formed as the imbalance or as the -div F blend ...
subroutine rt_tau_tops(tautop)
Tau at the top face of every block row of the current band, per global column along hdim....
subroutine rt_band_blocks(ib)
List the blocks of this rank that lie in band ib, with their block position (bi, bj),...
subroutine rt_sc_solve_wave()
Ray-by-ray KBA, with one ray at a time passing through the diagonal wavefront.
subroutine rt_wave_begin(nslot)
Prologue of both KBA schedules on level rt_sc_amr_level. It checks the setup, sets the x-distribution...
subroutine rt_sc_solve_gather()
Replicated gather, the original implementation, kept for comparison and not for production....
logical function rt_grid_index(xh, xv, ib, ig, jg)
Indices (ig, jg) on the replicated grid of band ib of the cell centred at xh along hdim and xv along ...
subroutine rt_wave_band_open(ib)
Open band ib. It lists and caches the band blocks and zeroes the accumulators.
subroutine, public rt_sc_solve()
Collective, once per step. For each band on rt_sc_amr_level it sweeps all rays and forms the heating ...
subroutine rt_sweep_one(iblk, k, iib)
Sweep block iblk for ray k in place on iib. With updim = 1 the block is transposed around the kernel,...
subroutine rt_seam_write(iib, igrid, ibi, sx, k)
Seam write. At the far domain edge the outflow of ray k is kept for the next solve....
subroutine rt_inflow_ghosts(islot)
Inflow intensity in slot islot. The ghosts beyond the band ends, and beyond the horizontal edges wher...
subroutine rt_exchange(nslot)
Ghost exchange of the nslot intensity columns of ps_rt from i_isw, with the physical boundaries left ...
subroutine rt_seam_read(iib, igrid, ibi, sx, k)
Seam read. At the domain edge where ray k enters, the wrapped upwind ghost column of the block takes ...
subroutine rt_sc_validate_pipe()
Validation of the pipelined KBA. The ray-by-ray Q is copied into i_iout and the pipelined Q left in i...
pure integer function wave_stage(b, n, s)
Position of a block counted from the upwind end of a row of n blocks, for a ray whose cosine along th...
subroutine rt_wave_end()
Epilogue. It hands the seam edges over to the next solve, restores the hydro datatype and releases th...
subroutine rt_sc_solve_wave_pipe()
Pipelined KBA. The direction-sign corners run together in batches of nisw slots, and within each corn...
subroutine rt_block_tau(iblk, tautop, taub)
Tau from the band top to every cell centre of block iblk, integrated down from the top face of the bl...
subroutine rt_add_moments(iblk, k, iib)
Add the weighted intensity iib of ray k on block iblk to J in i_qrad, Fy in i_frad and Fx in rtw_fx.
Parameters, resolved state and lifecycle of the short-characteristics (SC) radiative transfer,...
integer, public i_frad
double precision rg_dh
double precision, dimension(:), allocatable mu_h
Ray cosines along the RT grid axes (hdim, updim). They are (mux, muy) for updim = 2 and the swapped p...
integer, public rt_sc_nband
double precision, dimension(:), allocatable mu_v
integer, dimension(rt_sc_maxband) band_nv
integer rt_node_comm
Node-local communicator for the moment reduction of the gather. The ranks reach that reduction far ou...
logical, public rt_sc_distributed
The distributed KBA wavefront. Each rank sweeps only its own band blocks, one diagonal at a time,...
integer, public rt_sc_amr_level
The AMR level the RT runs on. The bands are held at this level so that the RT sees a uniform grid,...
double precision, public rt_wt_swp
integer, public rt_nsolve
double precision, public rt_wt_wave
logical, public rt_sc_active
True when the physics module has switched the RT on, with hd_radiative_transfer or mhd_radiative_tran...
logical, public rt_sc_flux_blend
rt_sc_flux_blend blends the thin heating c chi (J - B) with the thick -div F. rt_sc_cfl limits the he...
integer rg_nh
The RT grid on rt_sc_amr_level along (hdim, updim): cells across the domain, cells per block,...
subroutine rt_allreduce_chunk(a, n1, n2, comm)
In-place sum allreduce of a 2D array in chunks of whole columns of about 1 MB.
double precision, public rt_wt_red
integer rt_lead_comm
Communicator of one leader rank per node. The field gather sums within each node, then over the leade...
integer, public i_iout
character(len=10), dimension(rt_sc_maxband), public rt_sc_bc_min
double precision, public rt_wt_gath
Cumulative wall time of the field gather, the sweep, the moment reduction and the scatter,...
integer, public i_qrad
Extra-variable indices of the heating Q, the vertical flux F and the emergent intensity Iout,...
logical, public rt_sc_pipeline
The pipelined KBA. The four sweep corners run at once, each with its own intensity slot,...
double precision rg_dv
character(len=10), dimension(rt_sc_maxband), public rt_sc_bc_max
logical, public rt_sc_debug
Prints the cumulative RT timing every 10 solves.
logical, public rt_sc_valpipe
For validation, runs the ray-by-ray and pipelined waves on the same state and prints max|dQ|.
double precision time_rt
Definition mod_timing.t:19
subroutine timer_open(t0)
Open a timing window, whose length leaves out the EoS and ghost-cell time spent inside it,...
Definition mod_timing.t:66
double precision function timer_close(t0)
Wall time since timer_open, less the EoS and ghost-cell time spent in between.
Definition mod_timing.t:73
integer nwgc
Number of variables which need to be updated in ghost cells.